Ankündigung

Einklappen
Keine Ankündigung bisher.

Handling mit lib.my_asynchat

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

    [Codebeispiel] Handling mit lib.my_asynchat

    Guten Morgen liebe Gemeinde und die üblichen Verdächtigen.

    Für ein Plugin wollte ich statt einer socket-Verbindung auf lib.my_asynchat setzen hab da vorher aber noch ein Verständnisproblem.

    Derzeit baue ich die Verbindung immer wieder neu auf und ab. Im ungeschickten Rhythmus kann es aber vorkommen dass (durch Multithread) die Anfragen durcheinander kommen. Auf der Serverseite wird das alles richtig gemacht nur auf Client-Seite (smarthome.py) gibts dann Verwirrung.
    Also benötige ich einen "Dauerläufer" und das sollte lib.my_asynchat ja sein.

    Problem:
    Wenn ich nun zyklisch Werte vom Server abfragen will muss ich ja irgendwo den cycle definieren, mach ich das über das item oder kann ich das auch im Plugin machen ?
    Derzeit lese ich ich die items ein, gebe jedem eine Nummer teile den globalen Zyklus durch die Anzahl der items. Beim Erststart des Plugins fang ich dann eben mit item #1 an und erhöhe bei jedem Durchlauf. Wenn alle durch sind gehts wieder bei #1 weiter, könnte man sich doch sparen wenn jedes item einen eigenen cycle bekommt oder ?
    Umgezogen? Ja! ... Fertig? Nein!
    Baustelle 2.0 !

    #2
    Hi Mirko,

    das solltest Du am besten im Plugin machen. Siehe z.B. Onewire mit den cycle Methoden.

    Bis bald

    Marcus

    Kommentar


      #3
      Gut dann bleib ich bei meinem alten Ansatz, hatte schon versucht dem scheduler ein item mitzugeben ... fand der aber doof .

      Ansonsten verhält sich my_asynchat sehr schön wenn man mal dahinter gestiegen ist.
      Umgezogen? Ja! ... Fertig? Nein!
      Baustelle 2.0 !

      Kommentar


        #4
        So langsam beschleicht mich das Gefühl dass ich mit asynchat nicht das erreiche was ich gerne möchte. Es geht um ein reines "Frage-Antwort-Spiel".
        Ich brauch also die Antwort des Servers um vernünftig weiter zu arbeiten aber hier fehlt mir der Ansatz. Vielleicht kann da jemand helfen?

        Unten mal der Code-Auszug. Wenn ich manuell ein item ändere dann lande ich irgendwann in set_parameter(self, cmd, item, value) und die Antwort des Servers landet auch brav bei _parse_response ... soweit in Ordnung.

        Für den zyklischen Durchlauf lande ich via scheduler in refresh_attributes(self) und in genau dieser Funktion (später auch in check_set(self,item)) brauch ich zwingend die Antwort des Servers ... wie bekomme ich das hin ??? Also die Antwort aus _parse_response(self, data) nach refresh_attributes(self) ???


        Code:
        class ebusBase(lib.my_asynchat.AsynChat):
        	
        	def __init__(self, smarthome, host, port=8888):
        		self.host = host
        		self.port = int(port)
        		lib.my_asynchat.AsynChat.__init__(self, smarthome, host, port)
        		self.terminator = '\n\r'
        		smarthome.monitor_connection(self)
        	
        	def handle_connect(self):
        		self.discard_buffers()
        		# runs at (re)connection
        		# logger.debug('eBus: handle_connect')
        
        	def _send(self, request):
        		logger.debug("ebus: Sending request: {0}".format(request))
        		self.push(request)
        	
        	def found_terminator(self):
        		data = self.buffer
        		self.buffer = ''
        		self._parse_response(data)
        		
        	def _parse_response(self, data):
        		try:
        			logger.debug("ebus: Getting  answer: {0}".format(data[1:]))  ### remove first char - why ???
        		except:
        			pass
        	
        	def set_parameter(self, cmd, item, value):
        		logger.debug("eBus: set parameter: value:{0} cmd:{1} item:{2}".format(value,cmd,item))
        		request = "set " + cmd + " " + str(value)
        		self._send(request)
        		#self.check_set(item())
        
        	def check_set(self,item):
        		logger.debug("eBus: {0}".format(item()))
        		logger.debug("eBus: Check SET: value:{0} cmd:{1}".format(item,cmd))
        		request = item.conf['ebus_type'] +" " + cmd + " " + str(item)
        		#self._send(request)  
        		
        	def refresh_attributes(self):
        		self.refresh_id = 1	
        		for cmds in self._attribute:
        			if cmds[0] == "cycle":
        				request = cmds[0] + " " + cmds[1]     #build command
        			else:
        				request = "get" + " " + cmds[1]     #build command
        				
        			if self.refresh_id == self.id:
        				#answer = self._request(request)
        				answer = self._send(request)
        				answer = self.buffer
        				logger.debug("eBus: Parameter/Refresh/Answer {0}/{1}/{2}".format(self.id,self.refresh_id,answer))
        				## !!!!! UPDATE !!!!
        				#self._attribute[cmds](answer, 'eBus', 'refresh')   #according to item(answer)
        				return answer
        			else:
        				self.refresh_id +=1
        P.S.: Die Zählung von refresh attributes hat noch nen bug, aber das tut grundsätzlich nichts zur Sache und funktionierte schonmal ... irgendwie waren das zu viele Änderungen heute .
        Umgezogen? Ja! ... Fertig? Nein!
        Baustelle 2.0 !

        Kommentar


          #5
          Hi Mirko,

          Asynchat kommuniziert, wie der Name suggeriert, asynchron. Du möchtest aber eine syncrone Kommunikation. Das MPD Plugin ist für beides gleichzeitg konzipiert, dadurch wird es allerdings ein bisschen komplizierter.

          Wahrscheinlich ist es in Deinem Fall besser auf Asynchat zu verzichten.
          Du kannst trotzdem smarthome.monitor_connection(self) verwenden um die Verbindung zu monitoren und bei Bedarf neu aufzubauen. Dazu musst Du allerdings ein paar Methoden (z.B. connect) und Attribute (is_connected glaube ich) definieren. Entweder im Source-Code lesen oder bis morgen warten.

          Wichtig ist allerdings das locking, damit Du mit mehreren Threads keine Probleme bekommst.
          Suche mal nach threading.Lock im Code, das wird in vielen meiner Plugins verwendet. Wichtig ist das das Lock immer freigeben wird, sonst hast Du schnell ein Dead-Lock.

          Und das refresh_attributes würde ich über den scheduler periodisch aufrufen lassen.

          Wenn Du nicht wirklich weiterkommst, dann würde ich für Dich ein Grundgerüst schreiben. Das würde allerdings ein bisschen dauern, da ich momentan nicht so viel Zeit vor dem Computer habe.

          Bis bald

          Marcus

          Kommentar


            #6
            Ne passt schon Marcus ... ich wühl mich da mal durch ich versuch dann lieber knackige Fragen zu stellen .

            Ausgerechnet in das mpd-plugin hatte ich nicht reingesehen ansonsten gibts ja genug Anregungen im git.
            Umgezogen? Ja! ... Fertig? Nein!
            Baustelle 2.0 !

            Kommentar


              #7
              So neuer Versuch aber ich scheitere schon beim testen.

              Code:
              root@black-automation:~/smarthome/bin# ./smarthome.py -d
              2013-09-05 09:28:32,710 Main         INFO     Start SmartHome.py 0.9-160-g580d434+ -- smarthome.py:__init__:242
              2013-09-05 09:28:32,712 Main         DEBUG    Python 2.7.3 -- smarthome.py:__init__:243
              2013-09-05 09:28:32,714 Main         INFO     Init Scheduler -- scheduler.py:__init__:54
              2013-09-05 09:28:32,716 Main         INFO     Init Plugins -- smarthome.py:start:278
              2013-09-05 09:28:32,718 Main         DEBUG    Plugin: cli -- plugin.py:__init__:42
              2013-09-05 09:28:32,724 Scheduler    DEBUG    creating 5 workers -- scheduler.py:run:60
              2013-09-05 09:28:32,729 Main         DEBUG    Plugin: ebusd -- plugin.py:__init__:42
              [B][COLOR="DarkOrchid"]2013-09-05 09:28:32,732 Main         WARNING  Plugin ebusd exception: No module named ebusd -- plugin.py:__init__:56[/COLOR][/B]
              ../etc/plugin.conf
              Code:
              [ebusd]
                  class_name = ebusd
                  class_path = plugins.ebusd
                  host = localhost
                  port = 8888
                  cycle = 2
              ../plugins/ebusd/__init__.py
              Code:
              import sys
              import logging
              import socket
              import threading
              import struct
              import time
              import datetime
              import lib.my_asynchat
              import re
              
              logger = logging.getLogger('ebusd')
              
              class ebusd(ebusdbase):
              ....
              			
              class ebusdbase(lib.my_asynchat.AsynChat):
              ....
              Das funktioniert sonst doch auch ?
              Umgezogen? Ja! ... Fertig? Nein!
              Baustelle 2.0 !

              Kommentar


                #8
                Ich schon wieder.
                @Marcus:
                Ich sehe beim MPD-Plugin auch nur einen asynchronen Datenverkehr bzw. nirgends eine Stelle die direkt auf die Antwort zurückgreift.

                Ich bin schon kurz davor das hier zu nehmen
                20.14. telnetlib ? Telnet client ? Python v2.7.5 documentation

                Grüße
                Umgezogen? Ja! ... Fertig? Nein!
                Baustelle 2.0 !

                Kommentar


                  #9
                  Vorerst erledigt, sollte auch so funktionieren. Lediglich das monitoring der Verbindung muss ich noch mal testen.

                  Plugins dürfen doch schlafen (time.sleep) oder?
                  Oder kann man dem scheduler ein item mitgeben?


                  P.S.:
                  autopep8 und flake8 sind ja nützliche tools:
                  vorher:
                  https://github.com/JuMi2006/smarthom...us/__init__.py
                  nachher:
                  https://github.com/JuMi2006/smarthom...us/__init__.py


                  Kann man pull requests auch nochmal ändern?
                  Umgezogen? Ja! ... Fertig? Nein!
                  Baustelle 2.0 !

                  Kommentar


                    #10
                    Hi Mirko,

                    soweit ich weis kann man pull requests nicht mehr ändern. Ich habe Deinen Request übernommen. Danke dafür!
                    tools/ebusd2item.py habe ich aber gelöscht, da war nix drin.

                    time.sleep kann man verwenden. Man sollte es aber für vermeiden wenn mehr als ein zwei Sekunden warten möchte.
                    Bei einem Stop von SmartHome.py wird solange gewartet bis das Plugin fertigt ist. Wenn Du in einer Schleife steckst und ein z.B. ein time.sleep(10) aufrufen würdest, dann müsste man 10 Sekunden auf das Ende des Plugins/Threads warten. In dem Fall wird das Plugin/SmartHome.py hart gekillt.
                    Es wäre besser den Scheduler zu verwenden und die Methode damit zyklisch aufzurufen.

                    Bis bald

                    Marcus

                    Kommentar


                      #11
                      Beim zyklischen Aufruf der Methode habe ich aber keine Möglichkeit ein item mitzugeben und müsste dieses wieder intern "erzeugen" oder geht das irgendwie?

                      self._sh.scheduler.add('ebus', self.refresh(next_item), cycle=self._cycle)


                      Mit github muss ich noch ein wenig üben, die ebusd2item.py reiche ich mit doku mal nach.

                      Grüße
                      Umgezogen? Ja! ... Fertig? Nein!
                      Baustelle 2.0 !

                      Kommentar


                        #12
                        wieso setzt Du nicht self.next_item und verwendest das beim nächsten Aufruf?

                        bis bald

                        Marcus

                        Kommentar


                          #13
                          Hallo Martin,

                          ich hab da nochmal drüber nachgedacht, komme aber auf keinen Grünen Zweig.
                          Ich müsste dann jedem item wieder eine Nummer verpassen um das auszuwerten. Dann immer die Nummer checken, um eins erhöhen, Zählerüberlauf prüfen oder gehts einfacher und ich blicks nicht?
                          Umgezogen? Ja! ... Fertig? Nein!
                          Baustelle 2.0 !

                          Kommentar


                            #14
                            Hi Mirko,

                            wahrscheinlich meinst Du mich.

                            Ich habe zwei Änderungen am Code vorgenommen und kommentiert:
                            https://github.com/mknx/smarthome/co...6f5d872f2d21f8

                            Aber wie ich sehe gibt es noch mehr Probleme. Insbesondere das locking.

                            Wieso schläfst Du überhaupt in der run Methode? Verkraftet das die Gegenstelle nicht?

                            Ich würde den Code, wenn Du nichts dagegenhast mal ein bisschen aufräumen und verschlanken. Gib mir dazu aber bitte das OK.

                            Bis bald

                            Marcus

                            Kommentar


                              #15
                              Ja gerne.
                              Das locking hab ich vom Luxtronic übernommen, aber ehrlich gesagt hab ich das auch nicht 100% verstanden wozu es das braucht.

                              Ich schlafe in run derzeit nur weil es so bequem ist. Ich muss keine IDs zählen und jedes item wird regelmäßig aktualisiert. Der Socket Server ist der ebusd. Der sollte zwar stabil laufen, wir haben aber lange keinen Belastungstest mehr gemacht. Es kam bei hoher Buslast immer mal wieder zu Telegrammverlusten. Wenn ich da jetzt alles durchjage und die Wärmepumpe in dem Moment auch gesprächig ist kann es da eng werden. Dann fehlt im Zweifel ein Wert.

                              Die diffs werde ich mir ansehen um was mitzunehmen.
                              Umgezogen? Ja! ... Fertig? Nein!
                              Baustelle 2.0 !

                              Kommentar

                              Lädt...
                              X