Ankündigung

Einklappen
Keine Ankündigung bisher.

Logikengine, java powered

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    KNX/EIB Logikengine, java powered

    Hallo zusammen,

    momentan nutze ich den HS4 und dessen "Grafischer Logik Editor". So richtig zufrieden bin ich damit aber nicht. Es ist zwar praktisch auf fertige Komponenten/Module zurück zu greifen, aber was die Fehlersuche betrifft ist das für mich ein absoluter Horror.

    Als alternative fällt mir dann noch OpenHAB, linknx und Smarthome.py ein...

    OpenHAB:

    Da gibt's Rules und Scripts die die Logikengine darstellen. Allerdings kann ich mich mit der "Scriptsprache" irgendwie nicht anfreunden. Vielleicht bin ich zu sehr Java-Entwickler um mit der abgespeckten Sprache umgehen zu können?

    linknx:

    Hab ich mir ehrlich gesagt noch nicht genau angeschaut. Aber XML ist in der Hinsicht nicht so mein Freund.

    Smarthome.py

    Da geht das ganze schon recht gut. Python-powered. So wirklich überzeugen tut mich das aber auch nicht. Liegt vielleicht wieder an meinem Java-Virus den ich mit mir herumschleppe?!

    Alles in allem:

    Meine Anforderungen an eine Logik-Engine wären:

    * Anständiges debugging: Zum einen gute Log-Möglichkeiten in der Konsole/Logfile, zum anderen Live-Debugging ohne vorher eine riesen Umgebung aufsetzen zu müssen
    * Eine große Sprachfreiheit. Smarthome.py scheint hier von den genannten Alternativen schon das beste zu bieten.
    * Keine direkte Abhängigkeit zu eibd und Co. ... Ich glaube hier kann nur OpenHAB punkten?
    * Idealerweise individuelle physikalische Adressen für einzelne Logiken: Wenn im Gruppenmonitor alles von ein und derselben PA kommt, ist schwer zu sagen welche Logik das jetzt verursacht hat. Vor allem wenn mehrere Logiken die selben GAs ansteuern

    Meine aktuelle Überlegung wäre:

    Basteln eines kleinen Daemons (in Java ), welcher Logiken/Module laden und laufen lassen kann. Die KNX-Anbindung dann über Calimero, bzw. mein SlicKnx.
    Die Modularität ggf. über ein einfaches Plugin-Konzept (oder OSGi). Damit man mal eben schnell ein Script in der ssh-Shell anpassen kann: evtl. scriptbares Java (kein JavaScript), sowas wie http://beanshell.org/

    Das einzige das mit Kopfzerbrechen bereitet: Die Konfiguration der verwendeten GAs ...
    GAs hart im Code hinterlegen ist nicht so prickelnd, gerade wenn man doch mal ein klein wenig die GA-Struktur umbauen muss. OpenHAB hat das mit "Items" gelöst, die gleichzeitig noch die Funktion dahinter abstrahieren. Muss man aber wieder gesondert konfigurieren. Es wäre toll, wenn man hier einfach das KNX-Projekt aus der ETS nehmen könnte. Dann müsste einfach man beim erstellen der Logik den entsprechenden Namen verwenden. Allerdings müsste man dann erst die knxproj entsprechend parsen (--> extra Aufwand).

    Ob ich das alles so mache oder sein lasse: Noch kein Plan. Aktuell läuft's einigermaßen mit dem HS4. Aber wie gesagt, so richtig glücklich bin ich noch nicht. Geht's hier anderen genau so?

    Gruß
    Alex



    #2
    Hallo,

    mit ergeht es genau wie dir.

    Ich hatte gehofft, dass man vielleicht das Scripting in OpenHAB durch Java ersetzen kann.
    Bin aber noch nicht dazu gekommen mich dementsprechend einzuarbeiten.

    Kommentar


      #3
      Das geht bestimmt. Allerdings fand ich jetzt OpenHAB zuletzt etwas "aufgeblasen". Wer viele Technologien bunt mischt ist damit sicher gut bedient.

      Mit meiner Entwicklung für Helios, eKey und zuletzt auch meiner Dimlex-Heizungsanlage, habe ich alle im Haus vorhandenen Technologien und Systeme auf KNX adaptiert.
      OpenHAB wäre dann nur noch für KNX zuständig. Aber auch hier hab ich viele Stunden mit debugging zugebracht die meiner Meinung nach hätten entfallen können, wenn das kein so super-generisches System wäre. Mit dem HS z.B. bin ich ehrlich gesagt schneller zu einem funktionierenden, runden System gekommen (inkl. QC-Visu) als mit OpenHAB.

      Kurzum: OpenHAB macht vieles anders als/besser als der HS. Aber dafür hat es für mich wieder genau so viele Nachteile Probleme wie der HS selbst, so dass sich die Problemsituation eher verlagert statt zu verbessern.

      Kommentar


        #4
        Ich hab mal angefangen einen KNX-Project-File Parser zu bauen. Der ist soweit auch mal fertig. Damit kann ich aus einem ETS4 Projekt-Export die Namen+Adressen der Geräte, sowie die Namen+Adressen+Datenpunkttypen der Gruppenadressen extrahieren.

        Damit wäre es nun ohne großen Aufwand möglich in der noch zu bastelnden Logik-Engine per "Name" auf Geräte und Gruppenadressen zuzugreifen und auch gleich die passenden Datentypen ermitteln.

        Damit könnte man, ohne die Logik anpassen zu müssen, Gruppenadressen und Geräte "umsortieren" ohne den Logik-Code anfassen zu müssen. Ein weiterer kleiner Vorteil: Man kann ohne eine vorkonfiguration loslegen: Einfach den Namen wie man ihn in ETS eingerichtet hat verwenden.

        Mal schauen wie ich die nächsten WOchen nebenher Zeit habe um da mal einen Prototypen zu basteln. Das erste das ich damit realisieren würde ist die sonnenstandsgeführte Verschattung. Das in HS verwendete Sonnenstandsmodul habe ich schon in Java nachgebaut.

        Zuletzt geändert von tuxedo; 17.07.2015, 06:42.

        Kommentar


          #5
          Ich hatte mal 15min Zeit um mich ein wenig mit Beanshell zu beschäftigen. Anfangs war ich skeptisch, aber jetzt finde ich's toll.

          Es ist recht einfach zwischen Java und dem Beanshell-Script zu interagieren. Erste Tests sind also vielversprechend. Hab mal ein ersten exemplarisches, realitätsnahes Script für die kommende Logik-Engine gebaut. MIt den vielen Kommentaren ist es vielleicht auf den ersten Blick doch etwas "komplex". Aber seht selbst:

          Code:
          // Damit dises Script auch als Logik genutzt werden kann
          import de.root1.beanshelltest.Logic;
          // Damit man einfachen Zugriff auf die Gruppenadressen via Namen/Bezeichnung aus ETS nutzen kann
          static import de.root1.beanshelltest.GaProvider.*;
          // Damit man auf den Bus schreiben kann
          static import de.root1.beanshelltest.KnxAccess.*;
          
          // Bus-Verbindung holen
          knx = getKNX("1.1.100"); // Absender-PA dieses Scripts
          
          gaLichtRaumschaltungBadEG = getGA("Licht Raumschaltung Bad EG");
          gaPräsenzBadEG = getGA("Präsenz Bad");
          
          boolean letztePräsenz = false;
          
          // Bekannt geben auf welche GAs wir vom Bus hören wollen
          public String[] getInterestedGAs() {
              return new String[]{gaPräsenzBadEG};
          }
          
          // auf eingehende GA-Events reagieren
          public void knxEvent(GroupAddressEven event) {
          
              // negative Flanke erkennen
              if (letztePräsenz == true && event.asBool() == false){
                  knx.writeBoolean(false /* kein Response */, gaLichtRaumschaltungBadEG, false /* Licht aus */);
              }
          
              letztePräsenz = event.asBool();
              
          }
          
          // damit dieses Script von der Script-Engine auch als "Logic" erkannt wird bzw. genutzt werden kann
          return (Logic) this;
          Ergänzende Erklärungen dazu:

          Die "import"-Zeilen sind notwendig, damit das Script von der Engine zum einen richtig benutzt werden kann, und zum anderen, damit man vom Script aus Zugriff auf die Engine hat.

          "knx = getKNX(...)" wird benötigt, damit man auf den Bus schreiben kann. Das tolle hierbei wird sein: Man kann eine für das Script eigene Physikalische Adresse angeben, und somit später mit dem Busmonitor nachverfolgen "wer" da was auf den Bus sendet. Sprich: Das Script als solches lässt sich im Busmonitor identifizieren.

          "gaXXXXXXXXXXXX" sind Variablen für die Gruppenaddressen, welche mit Hilfe von "getGA(...)" dynamisch gefüllt werden. Man muss nur den Namen bzw. die Bezeichnung der GA gemäß konfiguration in ETS angeben. Fertig.

          "getInterestedGAs() ...": Damit konfiguriert man auf welche GAs man vom Bus hören möchte (in HS gesprochen: "Eingangsbox"). Hier kann man wieder die gaXXXXXXXXx Variablen nutzen und quasi beliebig viele GAs angeben.


          "knxEvent(....).....": Wenn eine zuvor bekannt gegebene GA vom Bus empfangen wird, dann wird diese Methode ausgelöst. D.h. hier findet die (eventbasierte) Verarbeitung der Logik an.

          In diesem Beispiel geht darum, das Licht im Bad automatisch wieder aus zu machen wenn keiner mehr im Raum ist. Normalerweise schaltet der PM das Licht ein und wieder aus. Aber es kommt auch vor, dass man selbst mal das Licht einschaltet und es dann vergisst aus zu schalten. Über den
          "Präsenz"-Ausgang des PMs schaltet dieses Script das Licht wieder aus. Dazu wird geschaut ob die "Flanke" von "1" auf "0" springt und dann das Licht ausgeschaltet.


          die letzte Zeile "return ..." ist wieder (wie die imports) fix vorgegeben und notwendig.

          Das war jetzt nur ein einfaches Beispiel. Es ist noch viel mehr möglich. Man kann auch Threads und Timer starten, so dass Verzögerungen etc. umgesetzt werden können. Auch kann man im Script wohl weitere Scripts laden, womit man eine Art "Utility-Script-Bibliothek" aufbauen könnte. Ähnlich zu dem was der HS mit den vorgefertigten Modulen anbietet. Ein Beispiel wäre hier die Sonnenstandsberechnung. Die könnte man als fertiges Script einfach im eigenen Script inkludieren und nutzen.

          Was ich noch probieren muss ich das Logging, so dass man auch anständig Feedback zur Laufzeit erhält was das Script jetzt gerade macht/gemacht hat.

          Ein weiterer für mich wichtiger Punkt wird sein: Scripts zur Laufzeit austauschen, hinzufügen oder entfernen. Sollte kein großes Problem sein.

          Zum Schluss das Script nochmal ohne die ganzen Erklär-Kommentare:

          Code:
          import de.root1.beanshelltest.Logic;
          static import de.root1.beanshelltest.GaProvider.*;
          static import de.root1.beanshelltest.KnxAccess.*;
          
          
          knx = getKNX("1.1.100"); // Absender-PA dieses Scripts
          
          gaLichtRaumschaltungBadEG = getGA("Licht Raumschaltung Bad EG");
          gaPräsenzBadEG = getGA("Präsenz Bad");
          
          
          letztePräsenz = false;
          
          
          public String[] getInterestedGAs() {
              return new String[]{gaPräsenzBadEG};
          }
          
          
          public void knxEvent(GroupAddressEven event) {
          
              // negative Flanke erkennen
              if (letztePräsenz == true && event.asBool() == false){
                  knx.writeBoolean(false, gaLichtRaumschaltungBadEG, false /* Licht aus */);
              }
          
              letztePräsenz = event.asBool();
              
          }
          
          // damit dieses Script von der Script-Engine auch als "Logic" erkannt wird bzw. genutzt werden kann
          return (Logic) this;
          Ist nun etwas leserlicher/übersichtlicher, oder?

          Kommentar


            #6
            Der Ansatz von dir die Logik in die BeanShell zu verlagern gefällt mir sehr gut.

            Ebenfalls finde ich es gut, wenn nicht die Gruppenadressen in der Engine von Hand nachgelegt werden müssen!
            Statt der Identifizierung über Strings wäre es vielleicht Interessant mit deinem KNX-Project-File Parser ein Java-Interface zu erzeugen.
            Dann müssen die Namen nicht getippt werden sondern können in Eclipse einfach mit der Autovervollständigung ausgewählt werden.

            Code:
            public interface GroupAddress {
               public static final String PRAESENZ_BAD = "2/1/4";
            }
            Hut ab mit welchem Ehrgeiz du deine verschieden privaten Softwareprojekte vorantreibst!

            Kommentar


              #7
              Toll dass die der Ansatz gefällt. Freue mich immer über Feedback und Kritik. Nur so wird die Software besser.

              Statt der Identifizierung über Strings wäre es vielleicht Interessant mit deinem KNX-Project-File Parser ein Java-Interface zu erzeugen.
              Wäre wohl ohne Probleme machbar.

              Dann müssen die Namen nicht getippt werden sondern können in Eclipse einfach mit der Autovervollständigung ausgewählt werden.
              Wie vertraut bist du mit Beanshell? Arbeite beruflich mit Eclipse, private mit Netbeans. Aber ein "beanshelleditor" der Autovervollständigung kann ist mir noch nicht begegnet. Hast du da einen heißen Tipp für mich?

              Hut ab mit welchem Ehrgeiz du deine verschieden privaten Softwareprojekte vorantreibst!
              Danke für die Blumen. Grund für meinen Antrieb ist primär die unzufriedenheit bzgl. der bereits verfügbaren Lösungen. Einige Nachbarn sind ganz begeistert von unserer KNX-Installation. Aber wenn die Spitz kriegen dass die sonnenstandsgeführte Verschattung (für mich ein tolles Feature und bei den Nachbarn meist in "Kinnlade klappt nach unten"-Auslöser) hier und da blödsinn macht, dann macht das ganze wenig Sinn.
              Der HS kostet ein sch**ßgeld, kann eigentlich ziemlich viel, aber beim Logiken erstellen sind entweder meine Ansprüche zu hoch, oder die Fähigkeiten des HS sind zu begrenzt. Ich vermute eher letzteres. Und dann ärgert mich der Preis wieder ...



              [update]
              Gerade schnell gegoogelt: Beanshell-Editor für Eclipse: Eclipse-Shell ... http://eclipse-shell.sourceforge.net/ Allerdings ist die Seite gerade "down" :-(

              Wenns da noch was für Netbeans gäbe .... Wäre super.
              Auf der anderen Seite: Für Anfänger ist das wohl nix. Da wäre ein "Standalone" Editor der Autocompletion für die wichtigsten Klassen hat wohl besser. Aber sowas müsste man wohl erst entwickeln :-(

              Zuletzt geändert von tuxedo; 17.07.2015, 10:15.

              Kommentar


                #8
                Selbst habe ich noch nichts mit der Beanshell gemacht.
                Beruflich bin ich momentan eher C/C++ unterwegs.

                Falls das Eclipse Beanshell Editor Plugin was taugt, wäre das eine super Sache.
                Kann ich aber leider nicht beurteile.
                Wer freiwillig mit Java-Syntax Logiken aufbauen möchte kennt eigentlich immer auch Eclipse

                Kommentar


                  #9
                  Wer freiwillig mit Java-Syntax Logiken aufbauen möchte kennt eigentlich immer auch Eclipse
                  Der "Profi" schon. Da die Java-Syntax aber an und für sich nicht sooo schwierig ist, könnte sich da durchaus auch ein Anfänger mit beschäftigen. Und Eclipse ist an und für sich ein "Monster" ---> Gerade für Anfänger eine entsprechend große Hürde.

                  Hab aber nebenher etwas gegoogelt wie es mit Syntax-Highlighting und AutoComplete im "Selbst-Implementierungs-Versuch" steht. Schaut ganz gut aus. Ich experimentiere da mal etwas. Vielleicht krieg ich ja ohne viel Aufwand einen kleinen Script-Editor hin der das beherrscht. Beanshell hat noch eine kleine Parser-Klasse die das Script auf Syntax-Fehler untersucht. Damit könnte man auch gleich den Code etwas checken.

                  *Hammer* Mir kommt da gerade noch eine Idee:

                  Da das Script ja bekannt geben kann für welche GA es sich interessiert und ich weiß welche GA welchen DPT braucht, könnte man auch gleich noch einen Script-Tester integrieren, mit dem man das Script mit Fake-KNX-Events auf den passenden GAs befeuert. Dank kenntnis des DPT kann das Toll einem da bei der Eingabe entsprechend unterstützen.
                  Fazit: Beanshell checkt die Syntax und mit dem Fake-KNX-Event kann man gleich den Code in einer Test-Laufzeitumgebung auf Semantik-Fehler prüfen.

                  Wenn der Tag doch nur mehr Stunden hätte ...

                  Kommentar


                    #10
                    Hey, das ist ja wirklich nicht soooo schwer:

                    Hab nun eine Texteditor-Komponente die prinzipiell Syntax-Highlighting beherrscht.
                    syntax-editor.PNG

                    Auch die Autovervollständigung geht schon rudimentär:
                    autocomplete.PNG

                    Hier und da gibt's sicher noch Ecken und Kanten die man rund feilen muss. Aber allzuviel fehlt nicht mehr dass das ein erster brauchbarer Editor für die Beanshell-Scripts ist:

                    Menuleiste, Speichern/Öffnen-Dialog, Syntax-Check-Button, KNX.project File (neu) Einlesen-Button.
                    Zuletzt geändert von tuxedo; 07.08.2015, 08:13.

                    Kommentar


                      #11
                      So, mal wieder ein kleines Update:

                      Die Idee ist nach wie vor nicht verworfen. Stattdessen wächst und gedeiht sie..

                      Aktueller Stand:

                      Am "Editor" hat sich nichts geändert. Der ist mal wie er ist. War ja auch nur ein Test wie einfach das geht.
                      Am "Framework" hat sich aber etwas getan:

                      Ich habe Apache Felix als OSGi Framework für mich entdeckt und angefangen Bundles dafür zu schreiben.

                      Nach ersten anfänglichen Problemen mit dem Logging in OSGi (wenn man OSGi mal einigermaßen verstanden hat wird vieles einfacher), hab ich nun ein erstes Logik-Engine-Bundle das Scripts in einem Verzeichnis sucht, lädt und prinzipiell ausführt.

                      Die Idee ist nach wie vor, dass die Engine das Script nach den "relevanten GAs" frägt (das sind die GAs auf die das Script reagieren soll), und die Engine dann bei Auftreten eines Events an der entsprechenden GA das Script hervor holt und das GA-Event zum verarbeiten hinein steckt.

                      Was noch nicht ganz klar ist, wie das mit Threads und Timern funktioniert bzw. funktionieren soll. Theoretisch kann man im Script Threads selbst starten etc. Oder aber ich mache eine Schnittstelle mir der das Script seine nebenläufigen Aktionen zum Ausführen an die Engine übergibt, und diese dann die passenden Threads/Timer startet und überwacht. Damit wäre dann das Script ggf. etwas übersichtlicher (weil der ganze Thread-Management-Code wegfällt). Mal schauen. Vielleicht mach ich auch einfach beide Möglicheiten.

                      Bzgl. Logging aus dem Script heraus muss ich mir auch noch was einfallen lassen: Loggen geht prinzipiell schon. Aber ich hätte gern dass man anhand der Logausgabe sieht, wo geloggt wurde. D.h. welches Script, welche Methode im Script und am besten in welcher Zeile. Das macht das Fehlersuchen einfacher. Aktuell ist es noch so, dass als Log-Quelle immer ein Java-Reflection-Aufruf genannt wird.

                      Stay tuned ....

                      P.S. Die Startup-Zeit ist äußerst gering. Auf meinem in die Jahre gekommenen Mini-ITX Server sind es nur 2-3sec. Auf einem RaspberryPi ca. 10sec.

                      Kommentar


                        #12
                        Gute Nachrichten: Der Wald lichtet sich langsam ...

                        Was aktuell schon geht:

                        - Einlesen der knxproj-Datei die man mit ETS exportiert hat --> Damit hat man Gruppenadressen und zugehörige DPTs, soweit aus dem KNX Projekt überhaupt herauslesbar (da geh ich gleich nochmal genauer darauf ein...)
                        - laden beliebig vieler Scripts die auf Events an einer oder mehreren GA warten und dann entsprechend darauf reagieren
                        - Logging, prinzipiell auch mit Zeilennummer, Methodenname und Filename des Scripts
                        - Verwenden der Gruppenadressen anhand ihres Namens aus ETS (siehe Beispielscript unten)
                        - Prinzipielles ausführen der Scripts ... d.h. das eigentliche Ziel, nämlich Logik scriptbar mit Java umzusetzen, ist erreicht.

                        Was noch fehlt, bzw. als nächstes kommt:

                        - Laden/Entladen von Scripts zur Laufzeit: So muss man die Anwendung nicht jedesmal neu starten wenn man etwas ändert. Einfach das Script ändern, speichern und es wird neu geladen. Oder löschen, dann wird's entladen. Oder ein neues hinzufügen, dann wird dies geladen.
                        - sauberes Logging... Das ist bis jetzt rudimentär und experimentell. Hier muss noch ein wenig aufgeräumt und die Verwendung etwas intuitiv gestaltet werden. Vor allem was die Log-Konfiguration (von welchem Script will ich welche Info sehen...)
                        - den KnxProjectReader etwas aufräumen: Der ist im default-Zustand sehr "gesprächig"... (hab die Ausgabe unten massiv eingekürzt)
                        - Ein paar OSGI Feinheiten.... Es funktioniert, aber beim bauen gibt's noch ungeklärte Fehlermeldungen
                        - alles etwas hübscher, einfacher und gut konfigurierbar machen
                        - Ein vernünftiger Name für das Ding. Bisher heisst es "KNX Logic Engine", kurz KLE ... Gefällt mir aber noch nicht. Wer Ideen hat: Her damit...
                        - Einfacher Zugriff auf die GAs wie oben schon mal vorgeschlagen

                        Zukunftsmusik:

                        - Hot-Standby-System: Zwei Instanzen der Software auf unterschiedlichen Geräten/Rechnern: Fällt eine Instanz aus (weil z.B. Wartung oder Hardware-Defekt), übernimmt die zweite Instanz die Arbeit, bis die erste Instanz wieder da ist. Die notwendige Technik (Software-Module) hierfür hab ich schon. Ich muss sie nur noch einsetzen...
                        - Ein OSGI-Bundle mit dem man die Anwendung zur Visu bzw. als Visu-Backend "aufblasen" kann.
                        - Anbindung von KNX-Software-Geräten (HeliosKwlRemote, ekey-decoder, (Dimplex-)Modbus2KNX, ..., siehe Signatur): Einfach in den Bundle-Ordner mit reinwerfen und schon laufen die Software-Geräte mit. --> Das ist dann ein wenig wie bei OpenHAB. Nur mit dem Unterschied, dass hier nicht versucht wird alles über einen internen Software-Bus zu "routen" der über allen Bus-Systemen steht. Stattdessen wird KNX als gemeinsames Rückrat verwendet. Getreu dem Motto: Was nicht KNX ist, wird KNX-fähig gemacht.

                        Für die Praktiker unter den Mitlesern, hier mal eine exemplarische Ausgabe der Logik-Engine beim Start und im Betrieb:

                        Code:
                        2015-08-07 08:32:47.343 INFO [FelixStartLevel] java.util.logging.LogManager$RootLogger.log: Set formatter to: de.root1.logging.JulLogFormatter
                        2015-08-07 08:32:47.455 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.start: SLF4J: Started ...
                        2015-08-07 08:32:47.457 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.initGa: Reading knx project data ...
                        2015-08-07 08:32:49.332 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.readProjects: Found project: Project{internalID=P-05FA, name=FooBarTestProjekt, lastModified=Tue Jul 14 13:26:24 CEST 2015, projectStart=Sat Jun 01 15:46:25 CEST 2013}
                        
                        2015-08-07 08:32:56.033 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.11, name=BE1}
                        2015-08-07 08:32:56.034 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.12, name=LED1 Bilton}
                        2015-08-07 08:32:56.035 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.13, name=LED2 Bilton}
                        2015-08-07 08:32:56.036 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.14, name=HA1}
                        2015-08-07 08:32:56.037 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.15, name=JAL1 EG}
                        2015-08-07 08:32:56.037 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.16, name=JAL2 DG}
                        2015-08-07 08:32:56.038 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.17, name=JAL3 DFF}
                        2015-08-07 08:32:56.039 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.18, name=SA1}
                        2015-08-07 08:32:56.040 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found device: Device{address=1.1.30, name=PM Technik}
                        .
                        .
                        .
                        2015-08-07 08:32:56.063 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=0/0/1, name=Test, mainType=0, subType=0}
                        2015-08-07 08:32:56.064 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/100, name=Präsenz ALLE, mainType=0, subType=0}
                        2015-08-07 08:32:56.064 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/13, name=Präsenz Flur DG, mainType=1, subType=1}
                        2015-08-07 08:32:56.065 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/12, name=Präsenz Büro, mainType=0, subType=0}
                        2015-08-07 08:32:56.066 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/11, name=Präsenz Gast, mainType=0, subType=0}
                        2015-08-07 08:32:56.066 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/10, name=Präsenz Kind2, mainType=0, subType=0}
                        2015-08-07 08:32:56.067 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/9, name=Präsenz Kind1, mainType=0, subType=0}
                        2015-08-07 08:32:56.068 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/8, name=Präsenz Bad DG, mainType=0, subType=0}
                        2015-08-07 08:32:56.068 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/7, name=Präsenz Flur EG, mainType=1, subType=1}
                        2015-08-07 08:32:56.069 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/6, name=Präsenz Küche, mainType=0, subType=0}
                        2015-08-07 08:32:56.069 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/5, name=Präsenz Essen, mainType=1, subType=1}
                        2015-08-07 08:32:56.070 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/4, name=Präsenz Wohnen, mainType=1, subType=1}
                        2015-08-07 08:32:56.071 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/3, name=Präsenz Schlafzimmer, mainType=0, subType=0}
                        2015-08-07 08:32:56.072 INFO [FelixStartLevel] de.root1.ets4reader.KnxProjReader.<init>: Found groupaddress: GroupAddress{address=5/0/2, name=Präsenz Bad, mainType=1, subType=1}
                        .
                        .
                        .
                        2015-08-07 08:32:56.463 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.initGa: Reading knx project data ... *DONE*
                        BeanShell Interpreter running...
                        2015-08-07 08:32:56.807 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.start: Loading script: ./scripts/logic_myscript.bsh
                        2015-08-07 08:32:57.201 INFO [FelixStartLevel] de.root1.kle.LogicLogger.info: [Testlogger] Loading...*done*
                        2015-08-07 08:32:57.205 INFO [FelixStartLevel] de.root1.kle.LogicContainer.<init>: Script logic_myscript.bsh is interested in: 5/0/2
                        2015-08-07 08:32:57.207 INFO [FelixStartLevel] de.root1.kle.LogicLogger.info: [Testlogger] Loading...*done*
                        2015-08-07 08:32:57.209 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.addToMap: Creating new gaLogicMap list for 5/0/2
                        2015-08-07 08:32:57.210 INFO [FelixStartLevel] de.root1.kle.KnxLogicEngineBundle.addToMap: Adding LogicContainer(logic_myscript.bsh) to list for 5/0/2
                        ____________________________
                        Welcome to Apache Felix Gogo
                        
                        2015-08-07 08:33:07.274 INFO [Link notifier] de.root1.kle.KnxLogicEngineBundle$2.write: Forwarding GroupAddressEvent{source=1.1.250, destination=5/0/2, data=[1], type=GROUP_WRITE} to LogicContainer(logic_myscript.bsh)
                        2015-08-07 08:33:07.275 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Event happened!
                        2015-08-07 08:33:07.277 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Line: 21 : log ( )  : file=/opt/felix-framework-5.0.1/./scripts/logic_myscript.bsh
                        2015-08-07 08:33:07.285 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Speichere 'letzte präsenz': true
                        2015-08-07 08:33:09.883 INFO [Link notifier] de.root1.kle.KnxLogicEngineBundle$2.write: Forwarding GroupAddressEvent{source=1.1.250, destination=5/0/2, data=[0], type=GROUP_WRITE} to LogicContainer(logic_myscript.bsh)
                        2015-08-07 08:33:09.885 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Event happened!
                        2015-08-07 08:33:09.897 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Line: 21 : log ( )  : file=/opt/felix-framework-5.0.1/./scripts/logic_myscript.bsh
                        2015-08-07 08:33:09.910 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Negative Flanke erkannt
                        2015-08-07 08:33:09.925 INFO [Link notifier] de.root1.kle.LogicLogger.info: [Testlogger] Speichere 'letzte präsenz': false

                        Und hier das zugehörige Beispielscript:


                        Code:
                        // das muss jedes Script ganz oben stehen haben
                        source("./scripts/include/kle-common.bsh");
                        
                        // Logger und KNX Setup
                        log = getLogger("Testlogger");
                        knx = getKNX("1.1.100");
                        
                        // GAs holen
                        gaLichtRaumschaltungBadEG = getGA("Licht Raumschaltung Bad EG");
                        gaPräsenzBadEG = getGA("Präsenz Bad");
                        
                        // Hilfsvariablen... JUHU ...
                        boolean letztePräsenz = false;
                        
                        
                        // bekannt geben für welche GAs wir Events haben wollen
                        public String[] getInterestedGAs() {
                            log.info("Loading...*done*");
                            return new String[]{gaPräsenzBadEG};
                        }
                        
                        
                        // verarbeitung der KNX Events für oben genannte GA
                        public void knxEvent(GroupAddressEvent event) {
                        
                            log.info("Event happened!");
                            log();
                        
                            // negative Flanke erkennen
                            if (letztePräsenz == true && event.asBool() == false){
                                log.info("Negative Flanke erkannt");
                                knx.writeBoolean(false, gaLichtRaumschaltungBadEG, false /* Licht aus */);
                            }
                            log.info("Speichere 'letzte präsenz': {}", event.asBool());
                            letztePräsenz = event.asBool();
                        
                        }
                        
                        // das muss jedes Script ganz unten stehen haben
                        return (Logic) this;
                        Kurz gesagt: Das Script interessiert sich für die GA "Präsenz Bad". Dahinter steckt ein PM der im Bad hängt. Fällt die Flanke (d.h. die "präsenz fällt weg"), so wird das Licht im Bad ("Licht Raumschaltung Bad EG") abgeschaltet.
                        Normalerweise schaltet der PM das Licht ein/aus, helligkeitsabhängig. Aber falls man das Licht selbst eingeschaltet hat, und es nicht selbst wieder ausschaltet, so übernimmt die Logik dies präsenz-gesteuert.
                        Ist nur ein Beispielscript und hat keinen Anspruch auf "macht besonders viel Sinn".

                        Wenn jemand schon "alpha-testen" will: Einfach kurz melden ...

                        Gruß
                        Alex

                        Kommentar


                          #13
                          Ich hab gerade einen ersten Praxistest was da basteln von Scripts angeht hinter mir:

                          Ich hatte vor einigen Tagen schon das Sonnenstand-pur Logikbaustein für den HS, geschrieben von MatthiasS, in Java portiert. Hat nach einigen anfänglichen Schwierigkeiten dann auch funktioniert. Heute hab ich es nun auf Beanshell-Script portiert.

                          Hier das Ergebnis:

                          Code:
                          import java.util.Calendar;
                          import java.util.TimeZone;
                          
                          Sun() {
                              double altitude=0;
                              double azimuth=0;
                          
                              dump(){
                                  print("altitude: "+altitude+" azimuth: "+azimuth);
                              }
                          
                              return this;
                          }
                          
                          
                          SunPosition(Double lon, Double lat) {
                          
                              K = Math.PI / 180d;
                          
                              calc() {
                                  return calc(Calendar.getInstance(TimeZone.getTimeZone("GMT")));
                              }
                          
                              calc(Calendar c) {
                              
                                  s = Sun();
                          
                                  // Formel entnommen von "Logikbaustein - 19820", siehe Download-Bereich KNX-User-Forum.de
                                  double tageszahl = c.get(Calendar.DAY_OF_YEAR);
                          
                                  stunde = c.get(Calendar.HOUR_OF_DAY);
                                  minute = c.get(Calendar.MINUTE);
                          
                                  zeitdiff = stunde + minute / 60d - (15d - lon) / 15d - 11d;
                                  deklin = -23.45 * Math.cos(K * 360 * (tageszahl + 10) / 365);
                          
                                  x = Math.sin(K * lat) * Math.sin(K * deklin) + Math.cos(K * lat) * Math.cos(K * deklin) * Math.cos(K * 15 * zeitdiff);
                                  hoehe = Math.asin(x) / K;
                          
                                  y = -1 * (Math.sin(K * lat) * x - Math.sin(K * deklin)) / (Math.cos(K * lat) * Math.sin(Math.acos(x)));
                                  if (y > 0.9999) {
                                      y = 0.9999;
                                  } else if (y < -0.9999) {
                                      y = -0.9999;
                                  }
                                  azimut=0;
                                  if (zeitdiff<=-12) {
                                      azimut = 360-(Math.acos(y)/K);
                                  } else if (zeitdiff>-12 && zeitdiff<=0) {
                                      azimut = Math.acos(y)/K;
                                  } else if (zeitdiff>0 && zeitdiff<=12) {
                                      azimut = 360-(Math.acos(y)/K);
                                  } else if (zeitdiff>12) {
                                      azimut = Math.acos(y)/K;
                                  }
                          
                                  s.azimuth = azimut;
                                  s.altitude = hoehe;
                                  return s;
                              }
                          
                          
                          
                              dump() {
                                  print("SunPosition: lon="+lon+" lat="+lat);
                              }
                          
                              return this;
                          
                          }
                          
                          // Nur für Tests ...
                          sp = SunPosition(1.234, 5.687)
                          sp.dump();
                          sun = sp.calc();
                          sun.dump();
                          Beim portieren ist folgendes aufgefallen:

                          - Wenn man Fragment für Fragment portiert, vergisst man schnell mal die Variablen-Namen anzugleichen. Folglich gab es im Script Variablen, die nirgendwo deklariert und initialisiert waren. Die Fehlermeldung vom Beanshell-Interpreter war da zwar korrekt, aber hat wenig zur Fehlerindikation beigetragen. Ich habe Stück für Stück den Code auskommentieren und dann Stück für Stück wieder aktivieren müssen bis ich den Fehler eingegrenzt hatte.
                          - Beanshell-Objekte fühlen sich nicht an wie Java-Objekte: In Beanshell Java zu benutzen ist intuitiv. Das verhält sich fast 1:1 gleich wie direkt in Java. In Beanshell aber selbst eine "Klasse" definieren ist irgendwie "anders". Sieht man am Code oben: Kein "public class XYZ...", kein Konstruktur. Getter und Setter sind unnötig, da per-se die Variablen public sind, ...
                          - Wenn man nicht nur kleine einfache Scripts bastelt (wie mein Bad-Licht-Auto-Aus), wünscht man sich schnell eine IDE mit entsprechender Unterstützung.


                          Mal schauen wohin der Weg führt. Vielleicht lasse ich die Tür für JavaScript als Alternative besser noch offen?! Beanshell an sich ist halt doch schon ein paar Tage alt.

                          Gruß
                          Alex

                          Kommentar


                            #14
                            Ich hab jetzt ein paar Abende damit zugebracht die Logik-Engine weiter nach vorne zu treiben. Hat soweit auch geklappt. Aber es bleibt ein fader nachgeschmack bzgl. der in die Jahre gekommenen BeanShell-Script-Engine.

                            Java liefert mit Nashorn mittlerweile einen JavaScript-Interpreter mit. Der ist recht schnell und performant. Ist aber eben "JavaScript" und damit nicht mit "Java" gleichzusetzen (nein, nur weil die beiden Produkte "Java" im Namen haben, haben sie trotzdem nix miteinander zu tun).

                            Auf der einen Seite ist JavaScript recht verbreitet und viele finden sich darin schnell zurecht. Auf der anderen Seite ist die Interaktion mit der Java-Seite mehr als Umständlich und nicht so toll gelöst. Wenn man jetzt nicht immer von Java nach JavaScript und zurück Dinge aufrufen muss ist JavaScript wohl ganz okay. Aber für meine Anwendungszwecke eher nicht so doll.

                            Eine andere Script-Engine die Java spricht gibt's wohl nicht. ABER ich bin da auf eine Idee gekommen:

                            Man kann herkömmlichen Java Code auch zur Laufzeit "compilieren" und "laden". Damit würde könnte man quasi direkt "Java scripten":

                            Verzeichnis überwachen. Kommt eine neue Java Source File hinzu: Compilieren und prüfen ob die File "ladbar" ist und dann laden. Fertig ist die eigene Java Scripting-Engine.

                            Blöd nur dass das quasi ein eigenes Projekt wäre. Aber würde hier gut reinpassen.

                            Vorteile wären: Man kann jede x-beliebige IDE nutzen. Wer nicht ganz so firm ist mit einer Java IDE, der nutzt einen guten Texteditor mit Syntax-Highlighting (gedit, Notepad++, vi, ...).

                            Vorteile wären:

                            * Voller Java-Sprachumfang
                            * Gute Tool-Unterstützung (beliebige IDE, beliebiger Java-Debugger,...)
                            * Schnell: Einmal beim Start compiliert -> so schnell wie normaler Java Code
                            * Für Experten: Vorcompilierte Scripts mit eigenen Abhängigkeiten etc?!



                            Mal darüber nachdenken/schlafen...
                            Ideen und Anregungen sind wie immer willkommen.

                            Kommentar


                              #15
                              Zitat von tuxedo Beitrag anzeigen
                              Man kann herkömmlichen Java Code auch zur Laufzeit "compilieren" und "laden". Damit würde könnte man quasi direkt "Java scripten":
                              Ja, das geht sehr fein, hab ich auch in einem Projekt realisiert (der Kunde wollte die Software, an bestimmten Stellen, ohne neue Lieferung verändert haben)
                              lg
                              Stefan

                              Kommentar

                              Lädt...
                              X