Nachricht
  • EU e-Privacy Directive

    Diese Webseie nutzt Cookies zum managen der Authentication, Navigation, und weiterer Funktionen. Beim Benutzen unserer Webseite stimmen Sie zu, das wir Cookies nutzen und auf Ihrem Computer speichern.

    e-Privacy Directive Documente ansehen

    Sie haben Cookies abgeleht. Diese Entscheidung kann rckgngig gemacht werden.

Python Code: 3964r Treiber

Bewertung: 5 / 5

Stern aktivStern aktivStern aktivStern aktivStern aktiv
 

Show/Hidden python code

View source
  1.  
  2. #!usr/bin/python3
  3. # -*-coding:Utf-8 -*
  4.  
  5. # 3964r Protokoll Treiber für RS232
  6. # Michael Thelen OKT 2015
  7.  
  8. from serial import Serial
  9. from datetime import datetime
  10. import time as t
  11. import binascii
  12. import threading
  13. from schritte import stepchain
  14.  
  15. #---------------------------------------------------------------------------
  16. # Schrittkette für 3964r Protokoll
  17. # Grundklasse ist Stepchain, elementare Schrittkette
  18. #
  19. # Die Prozedur 3964R ist ein asynchrones, bitserielles Übertragungsverfahren. Über die Verbindung werden
  20. # Steuer- und Nutzinformationszeichen gesendet. Um jedes Zeichen beim Empfänger wiederzuerkennen,
  21. # und um die fehlerfreie Übertragung zu kontrollieren, werden den gesendeten Zeichen weitere Bits voranbzw.
  22. # nachgestellt. Die Reihenfolge der Bits auf der Leitung ist:
  23. # SA I0 I1 I2 I3 I4 I5 I6 I7 SO
  24. # SA = Startbit
  25. # In = Informationsbit Nr.
  26. # SO = Stoppbit
  27. #
  28. # Die Steuerzeichen für die Prozedur 3964R sind der Norm DIN 66003 für den 7-Bit-Code entnommen. Sie
  29. # werden allerdings mit der Zeichenlänge 8 Bit übertragen (Bit I7 = 0). Am Ende jedes Datenblocks wird zur
  30. # Datensicherung ein Prüfzeichen(BCC) gesendet.
  31. # Das Blockprüfzeichen wird durch eine exklusiv-oder-Verknüpfung über alle Datenbytes der
  32. # Nutzinformation, inclusive der Endekennung DLE, ETX gebildet.
  33. # Für die Informationszeichen ist kein Code vorgeschrieben (Codetransparenz).
  34. #
  35. # *****************************
  36. # Senden mit der Prozedur 3964R
  37. # Zum Aufbau der Verbindung sendet die Prozedur 3964R das Steuerzeichen STX aus. Antwortet das
  38. # Peripheriegerät vor Ablauf der Quittungsverzugzeit (QVZ) von 2 sec mit dem Zeichen DLE, so geht die
  39. # Prozedur in den Sendebetrieb über. Antwortet das Peripheriegerät mit NAK, einem beliebigen anderen
  40. # Zeichen (außer DLE) oder die Quittungsverzugszeit verstreicht ohne Reaktion, so ist der
  41. # Verbindungsaufbau gescheitert. Nach insgesamt drei vergeblichen Versuchen bricht die Prozedur das
  42. # Verfahren ab und meldet dem Interpreter den Fehler im Verbindungsaufbau.
  43. #
  44. # Gelingt der Verbindungsaufbau, so werden nun die im aktuellen Ausgabepuffer enthaltenen
  45. # Nutzinformationszeichen mit der gewählten Übertragungsgeschwindigkeit an das Peripheriegerät
  46. # gesendet. Das Peripheriegerät soll die ankommenden Zeichen in Ihrem zeitlichen Abstand überwachen.
  47. # Der Abstand zwischen zwei Zeichen darf nicht mehr als die Zeichenverzugszeit (ZVZ) von 220 ms
  48. # betragen.
  49. #
  50. # Jedes im Puffer vorgefundene Zeichen DLE wird als zwei Zeichen DLE gesendet. Dabei wird das Zeichen
  51. # DLE zweimal in die Prüfsumme übernommen.
  52. #
  53. # Nach erfolgtem senden des Pufferinhalts fügt die Prozedur die Zeichen DLE, ETX und BCC als
  54. # Endekennung an und wartet auf ein Quittungszeichen. Sendet das Peripheriegerät innerhalb der
  55. # Quittungsverzugszeit QVZ das Zeichen DLE, so wurde der Datenblock fehlerfrei übernommen. Antwortet
  56. # das Peripheriegerät mit NAK, einem beliebigen anderen Zeichen (außer DLE), einem gestörten Zeichen
  57. # oder die Quittungsverzugszeit verstreicht ohne Reaktion, so wiederholt die Prozedur das Senden des
  58. # Datenblocks. Nach insgesamt sechs vergeblichen Versuchen, den Datenblock zu senden, bricht die
  59. # Prozedur das Verfahren ab und meldet dem Interpreter den Fehler im Verbindungsaufbau.
  60. #
  61. # Sendet das Peripheriegerät während einer laufenden Sendung das Zeichen NAK, so beendet die
  62. # Prozedur den Block und wiederholt in der oben beschriebenen Weise.
  63. #
  64. # Beispiel für einen fehlerlosen Datenverkehr:
  65. # Prozedur 3964R Peripheriegerät
  66. # STX           ->
  67. #               <- DLE
  68. # 1. Zeichen    ->
  69. #               ->
  70. #               ->
  71. #               ->
  72. # n. Zeichen    ->
  73. # DLE           ->
  74. # ETX           ->
  75. # BCC           ->
  76. #               <- DLE
  77. #
  78. # ********************************
  79. # Empfangen mit der Prozedur 3964R
  80. # Im Ruhezustand, wenn kein Sendeauftrag und kein Warteauftrag des Interpreters zu bearbeiten ist, wartet
  81. # die Prozedur auf den Verbindungsaufbau durch das Peripheriegerät. Empfängt die Prozedur ein STX und
  82. # steht ihr ein leerer Eingabepuffer zur Verfügung, wird mit DLE geantwortet.
  83. #
  84. # Nachfolgende Empfangszeichen werden nun in dem Eingabepuffer abgelegt. Werden zwei aufeinander
  85. # folgende Zeichen DLE empfangen, wird nur ein DLE in den Eingabepuffer übernommen.
  86. # Nach jedem Empfangszeichen wird während der Zeichenverzugszeit (ZVZ) auf das nächste Zeichen
  87. # gewartet. Verstreicht die Zeichenverzugszeit ohne Empfang, wird das Zeichen NAK an das
  88. # Peripheriegerät gesendet und der Fehler an den Interpreter gemeldet.
  89. #
  90. # Mit erkennen der Zeichenfolge DLE, ETX und BCC beendet die Prozedur den Empfang und sendet DLE
  91. # für einen fehlerfrei (oder NAK für einen fehlerhaft) empfangenen Block an das Peripheriegerät.
  92. # Treten während des Empfangs Übertragungsfehler auf (verlorenes Zeichen, Rahmenfehler), wird der
  93. # Empfang bis zum Verbindungsabbau weitergeführt und NAK an das Peripheriegerät gesendet. Dann wird
  94. # eine Wiederholung des Blocks erwartet. Kann der Block auch nach insgesamt sechs Versuchen nicht
  95. # fehlerfrei empfangen werden, oder wird die Wiederholung vom Peripheriegerät nicht innerhalb der
  96. # Blockwartezeit von 4 sec gestartet, bricht die Prozedur 3964R den Empfang ab und meldet den Fehler an
  97. # den Interpreter.
  98. #
  99. # Beispiel für einen fehlerlosen Datenverkehr:
  100. # Prozedur 3964R       Peripheriegerät
  101. #                 <-      STX
  102. #   DLE           ->
  103. #                 <-    1. Zeichen
  104. #                 <-
  105. #                 <-
  106. #                 <-
  107. #                 <-    n. Zeichen
  108. #                 <-      DLE
  109. #                 <-      ETX
  110. #                 <-      BCC
  111. #   DLE           ->
  112. #
  113. # ************************
  114. # Initialisierungskonflikt
  115. # Antwortet ein Gerät auf den Sendewunsch (Zeichen STX) seines Peripheriegerätes innerhalb der
  116. # Quittungsverzugszeit QVZ nicht mit der Quittung DLE oder NAK, sondern ebenfalls mit dem Zeichen STX,
  117. # liegt ein Initialisierungskonflikt vor. Beide Geräte möchten einen vorliegenden Sendeauftrag ausführen.
  118. # Das Gerät mit der niederen Priorität stellt seinen Sendeauftrag zurück und antwortet mit dem Zeichen
  119. # DLE. Das Gerät mit der höheren Priorität sendet daraufhin seine Daten in der vorher beschriebenen
  120. # Weise. Nach dem Verbindungsabbau kann das Gerät mit der niederen Priorität seinen Sendeauftrag
  121. # ausführen.
  122. #
  123. # niedrige Priorität     höhere Priorität
  124. #   STX           ->
  125. #                 <-     STX  (Konflikt)
  126. #   DLE           ->
  127. #                 <-    1. Zeichen
  128. #                 <-
  129. #                 <-
  130. #                 <-
  131. #                 <-    n. Zeichen
  132. #                 <-      DLE
  133. #                 <-      ETX
  134. #                 <-      BCC
  135. #   DLE           ->
  136. #
  137. #
  138. # Klassendifinition für den 3964r Treiber
  139. # Der Treiber bedient eine Schnittstelle, welche definiert werden muss
  140. class Dust3964r (stepchain,Serial):
  141.     lock = threading.Lock()
  142.     
  143.     sendtry     = 0       # Anzahl der Sendeversuche
  144.     sendbuff    = b""     # Sendepuffer ist leer
  145.     readbuff    = b""     # Empfangspuffer
  146.     telegrammOut= []      # Sendetelegramm (Puffer)
  147.     MODE        = True    # Mit Blockprüfzeichen
  148.     CFG_PRIO    = True    # Treiber läuft mit hoher Priorität
  149.     CFG_PRINT   = True    # Modus Print eingeschalet (
  150.     ETX_EN      = False   # Sequenzer erkennt, ob ein ETX nach einem DLE gültig ist
  151.     BCC_EN      = False   # Sequenzer erkennt, das nach DLE, ETX nun das BCC folgen muss
  152.     RealRun     = True    # Lauf im Simulator = false, in Realität True
  153.     RUN         = False   # Der treiber läuft
  154.     STX         = b"\x02" # chr(0x02)
  155.     ETX         = b"\x03" # chr(0x03)
  156.     DLE         = b"\x10" # chr(0x10)
  157.     NAK         = b"\x15" # chr(0x15)
  158.     LOPRIO      = False   # niedrige Prio
  159.     HIPRIO      = True    # hohe Priorität
  160.     M3964       = False   # Treiber läuft als 3964 ohne BCC Blocksumme
  161.     M3964R      = True    # Treiber läuft als 3964r mit BCC Blocksumme
  162.  
  163.     def __init__ (self,port=None,baudrate=9600,QVZ=2.0,ZVZ=0.22,BWZ=4.0,CWZ = 3.0,SPZ=0.5,SLP= 1.4,MAXSEND=6,MAXCONNECT=6,PRIO=HIPRIO, MODE=M3964R):
  164.         # Initialisierung der Schrittkettenklasse
  165.         stepchain.__init__ (self)
  166.         # Initialisierung der SchnrittstellenKlasse
  167.         self.RS232     = Serial (port=port,baudrate=baudrate)
  168.         self.QVZ       = QVZ        # Quittungsverzug ist 2.0 Sekunden (Buderus Doku)
  169.         self.ZVZ       = ZVZ        # Zeichenverzugszeit 220ms (Buderus Doku)
  170.         self.BWZ       = BWZ        # Blockwartezeit 4.0 Sekunden (Buderus Doku)
  171.         self.CWZ       = CWZ        # Connectwartezeit 2.0 Sekunden (Wartezeit nach versuch fehlerhafter Verbindungsaufbau
  172.         self.SPZ       = SPZ        # SendePause zeit (nach einem erfolgreichem Senden warten bis nächstes Senden
  173.         self.SLP       = SLP        # Schlafenszeit vor dem Absenden vom DLE (muss klener als QVZ der Gegenseite sein)
  174.         self.MAXSEND   = MAXSEND    # Maximalanzahl Sendeversuche, danach wird das Telegramm verworfen
  175.         self.MAXCONNECT= MAXCONNECT # Anzahl maximaler Verbindungsaufbau Versuche
  176.         self.sendERR   = 0          # Sendefehler auf 0
  177.         self.connectERR= 0          # Verbindungsaufbau Fehler auf 0
  178.         self.RUN       = False      # Treiber in Stop
  179.         self.SendAtTime= 0          # Erlaube Senden ab dem Zeitpunkt
  180.         self.MODE      = MODE       # Treibermodus einstellen (Serienmäßig nach dem Start: 3964r mit Blocksumme
  181.         self.CFG_PRIO  = PRIO       # Modus einstellen
  182.         self.telegrammOut= []       # Ausgangspuffer ist leer
  183.         self.RS232.flushOutput ()   # puffer tillen
  184.         self.RS232.flushInput ()
  185.         self.RS232.write (self.NAK) # auf der schnittstelle mal blind am anfang ein NAK raushauen
  186.         
  187.  
  188.     # hiermit kann der Modus des Treibers umgeschaltet werden. der Aufruf kann nur nach dem INIT gemacht werden, nicht während des Laufens
  189.     # Mögliche Mode sind: PRIO = LO   : Bei einem INIT Konflikt stellt Treiber seinen Sendewunsch zurück
  190.     #                     PRIO = HI   : Bei einem Init Konflikt besteht der Treiber auf Bestätigung Sendebereitschaft durch die Gegenseite
  191.     #                     MODUS= 3964 : Übertragung ohne Blockprüfkennung BCC
  192.     #                     MODUS= 3964r: Übertragung mit Blockprüfkennung BCC
  193.     def mode (self,PRIO,MODE):
  194.         if not self.RUN:
  195.             self.CFG_PRIO= PRIO
  196.             self.MODE    = MODE
  197.  
  198.     # Berechnet das XOR CRC für den übergebenen buff
  199.     # buffer sollte ein bytestring sein
  200.     # der Rückgabewert ist ein bytestring
  201.     def crc (self,buffer):
  202.         bcc= 0x00
  203.         for c in buffer:
  204.             bcc ^= c
  205.         return bytes([bcc])
  206.  
  207.     # wandelt den buffer in den auszugebenen bytestring um
  208.     # erwartet buffer als bytestrng
  209.     # Rückgabewert ist ebenfalls ein bytestring
  210.     def outframe (self,buffer):
  211.         # Ein DLE in Datenstring führt zur Verdopplung von DLE
  212.         # DLE und ETX sind der Frame vom 3964r Protokoll
  213.         puffer= buffer.replace (self.DLE,self.DLE+self.DLE)+ self.DLE + self.ETX
  214.         # prüfen, welches Protokoll: 3964= ohne BCC, 3964r mit BCC
  215.         if self.MODE:
  216.             puffer+=self.crc (puffer)
  217.         return puffer
  218.  
  219.     # Befreit das Telegramm von dem 3964r Frame, prüft die Checksumme
  220.     # Der buffer muss ein bytestring sein
  221.     # Rückgabe: NONE, wenn irgendein Fehler aufgetreten ist, Telegramm verstümmelt, Checksum falsch
  222.     # Wenn alles OK, Rückgabe des Telegramms als Bytestring
  223.     def inframe (self,buffer):
  224.         stream= buffer
  225.         # BCC Kontrolle nur im 3964r modus
  226.         if self.MODE:
  227.             try:
  228.                 bufferBCC= stream[-1:]
  229.                 stream= buffer[:-1]
  230.             except: return None
  231.             # testen ob der Checksumme zu dem Stream passt
  232.             if bufferBCC!=self.crc (stream):
  233.                 return None
  234.         try:
  235.             # Nun prüfen, ob am Ende vom Stream DLW und ETX drinsind
  236.             if stream[-2:]!=self.DLE+self.ETX:
  237.                 return None
  238.         except: # Inframe buffer zu klein
  239.             return None
  240.         return stream [:-2].replace (self.DLE+self.DLE,self.DLE)    
  241.     
  242.     def sendstream (self,sendepuffer):       
  243.         buffer= self.outframe (sendepuffer)
  244.         self.RS232.write (buffer)
  245.         if self.CFG_PRINT:
  246.             print (" 10r",end="")
  247.             for c in buffer:
  248.                 print("%3.2X"% c + "s",end="")
  249.         # der puffer wurde über die rs232 ausgegeben
  250.  
  251.     # Fehler in der Kommunikation: NAK ausgeben
  252.     # Bei einem NAK wird immer auch ein flush ausgeführt
  253.     def errNAK (self):
  254.         self.RS232.flushOutput ()
  255.         self.RS232.flushInput ()
  256.         self.RS232.write (self.NAK+self.NAK+self.NAK)
  257.         self.setnewstep (0)
  258.  
  259.     # eine Verzögerungszeit für das nächste Senden wird definiert
  260.     def SetSendDelay (self,sec):
  261.         self.SendAtTime= t.time ()+sec
  262.  
  263.     # Read Success wird aufgerufen, wenn ein Telegramm erfolgreich eingelesen wurde
  264.     # Virtuelle Routine, muss überladen werden vom child    
  265.     def ReadSuccess (self,telegram):
  266.         pass
  267.  
  268.     # WriteFail wird aufgerufen, wenn win Telegramm verworfen wurde nach 6 Sendeversuchen
  269.     # Virtuelle Routine, muss überladen werden vom child    
  270.     def WriteFail (self,telegram):     
  271.         pass
  272.  
  273.     # Write Success wird aufgerufen, wenn ein Telegram erfolgreich versendet worden ist
  274.     # Virtuelle Routine, muss überladen werden vom child
  275.     def WriteSuccess (self,telegram):
  276.         pass
  277.  
  278.     # Routine Prüft, ob im Sendepuffer ein Auftrag vorhanden ist
  279.     def isJob (self):
  280.         return self.telegrammOut!=[]
  281.  
  282.     # Routine fügt einen neunen Sendeauftrag in den Puffer ein
  283.     def newJob (self,job):
  284.         Dust3964r.lock.acquire() # Thread blockieren, der part nun hier muss atomar sein
  285.         self.telegrammOut.append (job)
  286.         Dust3964r.lock.release() #
  287.         if self.CFG_PRINT:
  288.             print ("NEUER JOB EINGEGANGEN: ",job)
  289.  
  290.     # Routine nimmt den ältesten Sendeauftrag aus der Liste und gibt diesen Zurück
  291.     # existiert kein Job, wird NONE zurückgegeben
  292.     def getJob (self):    
  293.         if not self.isJob ():
  294.             return None   # Kein Job in der Liste
  295.         Dust3964r.lock.acquire() # Thread blockieren, der part nun hier muss atomar sein
  296.         job= self.telegrammOut [0]
  297.         self.telegrammOut= self.telegrammOut [1:]
  298.         Dust3964r.lock.release() #
  299.         if self.CFG_PRINT:
  300.             print ("JOB WIRD BEARBEITET: ", job)
  301.         return job
  302.         
  303.  
  304.     # Schritt 0: Der Grundschritt:
  305.     # Steht kein aktuelles Kommando zur Ausführung an und ist inWaiting() <>0 (Zeichen im Buffer)
  306.     # Dann neuer Schritt = 1 (Empfang überprüfen)
  307.     def schritt_0 (self):
  308.         if self.newstep: # Einmaliger Durchlauf in dem Schritt
  309.             # Initialisierung der Werte
  310.             if (self.sendERR==self.MAXSEND) or (self.connectERR==self.MAXCONNECT):
  311.                 self.WriteFail (self.sendbuff)
  312.                 self.sendbuff=b""        #
  313.                 if self.CFG_PRINT:
  314.                     print(t.strftime("%H:%M:%S")+"."+ "%6.6d"% datetime.now().microsecond + ": Telegramm verworfen nach " , self.MAXSEND , " Fehlversuchen")
  315.                 self.sendERR=0
  316.                 self.connectERR=0
  317.         if (self.sendbuff==b"") and self.isJob ():
  318.             job= self.getJob ()
  319.             if not (job is None):
  320.                 self.sendbuff=job
  321.         self.SEND_EN= (len(self.sendbuff)!=0) and (t.time ()>self.SendAtTime)        
  322.         if self.RS232.inWaiting () and self.RealRun:
  323.             # Es ist ein Zeichen im Empfangspuffer
  324.             # An dieser Stelle kann und darf es höchstens das Zeichen STX sein
  325.             char= self.RS232.read ()
  326.             if char != self.STX:
  327.                 # Es war kein STX, das ist auf jedenfall mal ein Fehler also: NAK senden
  328.                 if self.CFG_PRINT:
  329.                     print(t.strftime("%H:%M:%S")+"."+ "%6.6d"% datetime.now().microsecond + ":[RX]"+ "%3.2X"% ord (char) + "r 15s [NAK: STX-START]")                  
  330.                 self.errNAK ()
  331.             else: # Es war ein STX
  332.                 if not self.CFG_PRIO or not self.SEND_EN: # Treiber hat niedrige PRIO oder nix zum senden
  333.                     if self.CFG_PRINT:
  334.                         print(t.strftime("%H:%M:%S")+"."+ "%6.6d"% datetime.now().microsecond + ":[RX] 02r",end="")
  335.                     t.sleep (self.SLP) # Für die erlaubte Antwortzeit legt sich der Prozess schlafen    
  336.                     self.setnewstep (4) # Verbindungsaufbau 3964r läuft nun ready to receive
  337.                 elif self.SEND_EN:
  338.                     if self.CFG_PRINT:
  339.                         print(t.strftime("%H:%M:%S")+"."+ "%6.6d"% datetime.now().microsecond + ":[TX] 02r 02s",end="")
  340.                     self.RS232.flushOutput ()
  341.                     self.RS232.write (self.STX)
  342.                     self.setnewstep (1) # verbindungsaufbau mit Konflikt: wir wollen Senden mit Hiprio
  343.         else: # Es gibt kein Zeichen im Empfangspuffer
  344.             if self.SEND_EN: # wir haben was zu senden
  345.                 if self.CFG_PRINT:
  346.                     print(t.strftime("%H:%M:%S")+"."+ "%6.6d"% datetime.now().microsecond + ":[TX] 02s",end="")
  347.                 self.RS232.flushInput ()    
  348.                 self.RS232.flushOutput ()
  349.                 self.RS232.write (self.STX)
  350.                 self.setnewstep (3) # Verbindungsaufbau von uns kommt
  351.  
  352.     # Schritt 1: Senden (wir haben STX gesenden und erwarten ein DLE
  353.     # Es muss ein DLE innerhalb der quittungsverzugszeit kommen
  354.     # Alles was nicht DLE ist grund für ein NAK (Hi prio)
  355.     def schritt_1 (self):
  356.         if self.stepdauer>self.QVZ:
  357.             # Quittungsverzugszeit ist abgelaufen
  358.             if self.CFG_PRINT:
  359.                 print (" 15s [NAK: QVZ-START]")
  360.             self.connectERR +=1 # Verbindungsaufbaufehler um 1 erhöhen
  361.             self.SetSendDelay (self.CWZ)
  362.             self.errNAK ()
  363.         elif self.RS232.inWaiting ():
  364.             # Zeichen wurde eingelesen, es muss ein DLE sein
  365.             c= self.RS232.read ()
  366.             if c!= self.DLE:
  367.                 # Es war aber kein DLE
  368.                 if self.CFG_PRINT:
  369.                     print ("%3.2X"% ord (c) + "r 15s [NAK: DLE-START]")
  370.                 self.connectERR +=1 # Verbindungsaufbaufehler um 1 erhöhen
  371.                 self.SetSendDelay (self.CWZ)
  372.                 self.errNAK ()
  373.             else: # es war ein DLE, senden ausführen
  374.                 self.sendstream (self.sendbuff)
  375.                 self.setnewstep (2)
  376.  
  377.     # Schritt 2: Gesendeter Datenstream muss mit DLE vom Empfänger bestätigt werden
  378.     # DLE muss innerhalt der QVZ kommen
  379.     def schritt_2 (self):
  380.         if self.stepdauer>self.QVZ:
  381.             # Quittungsverzugszeit ist abgelaufen
  382.             if self.CFG_PRINT:
  383.                 print (" 15s [NAK: QVZ-BCC]")
  384.             self.sendERR +=1 # sendefehler um 1 erhöhen
  385.             self.SetSendDelay (self.BWZ)
  386.             self.errNAK ()
  387.         elif self.RS232.inWaiting ():
  388.             # Zeichen wurde eingelesen, es muss ein DLE sein
  389.             c= self.RS232.read ()
  390.             if c!= self.DLE:            
  391.                 # Es war aber kein DLE
  392.                 if self.CFG_PRINT:
  393.                     print ("%3.2X"% ord (c) + "r 15s [NAK: DLE-BCC]")
  394.                 self.sendERR +=1 # Verbindungsaufbaufehler um 1 erhöhen
  395.                 self.SetSendDelay (self.BWZ)
  396.                 self.errNAK ()
  397.             else: # es war ein DLE, Telegramm wurde erfolgreich versendet
  398.                 if self.CFG_PRINT:
  399.                     print (" 10r [OK]")
  400.                 self.WriteSuccess (self.sendbuff) # Virtuelle Routine
  401.                 self.sendbuff=b"" # Sendepuffer löschen, das telegramm austragen
  402.                 self.SetSendDelay (self.SPZ)
  403.                 self.setnewstep (0)
  404.  
  405.     # Schritt 3: Verbindungsaufbau von uns angestossen, wir wollen senden
  406.     # Von uns wurde ein STX gesendet, es darf nun als Antwort kommen:
  407.     # STX: Der Partner will selber senden
  408.     # DLE: Alles ok, wir senden
  409.     def schritt_3 (self):
  410.         if self.stepdauer>self.QVZ:
  411.             # Quittungsverzugszeit ist abgelaufen
  412.             if self.CFG_PRINT:
  413.                 print (" 15s [NAK: QVZ-DLE START]")
  414.             self.sendERR +=1 # sendefehler um 1 erhöhen
  415.             self.SetSendDelay (self.CWZ)
  416.             self.errNAK ()
  417.         elif self.RS232.inWaiting ():
  418.             c= self.RS232.read ()
  419.             if c== self.DLE:
  420.                 # Das eingelesene Zeichen ist ein DLE
  421.                 # wunderbar, alles, ok, wir können senden
  422.                 self.sendstream (self.sendbuff)
  423.                 # Nach dem Senden muss mit DLE vom empfänger bestätigt werden
  424.                 self.setnewstep (2)
  425.             elif c== self.STX:
  426.                 # Wir bekommen als Antwort auf unser STX ebenfalls ein STX zurück
  427.                 # der Klassische Initialisierungskonflikt
  428.                 if not self.CFG_PRIO:
  429.                     # Unser Treiber hat Low Prio also alles OK, wir müssen mit DLE antworten
  430.                     # Es folgt nun ganz normales Empfangen
  431.                     if self.CFG_PRINT:
  432.                         print (" 02r",end="")
  433.                     self.setnewstep (4)
  434.                 else: # Nu gibts ein Problem.
  435.                     # Unserer Treiber läuft auf High Prio, und die Gegenseite setzte auch ein STX ab
  436.                     # Die Gegenseite ist also auch im High Prio Mode
  437.                     # das ist eine etwas unkluge Sache in dem Moment
  438.                     # wir geben ein NAK raus und initialieren neu
  439.                     # REV 2. wir geben trotz High Prio nach und senden ein DLE
  440.                     #if self.CFG_PRINT:
  441.                     #    print (" 02r",end="")
  442.                     #self.setnewstep (4)   
  443.                     
  444.                     if self.CFG_PRINT:
  445.                         print (" 02r 15s [NAK: STX-STX PRIO]")
  446.                     self.connectERR +=1 # connectfehler um 1 erhöhen
  447.                     self.SetSendDelay (0)
  448.                     self.errNAK ()
  449.             else:
  450.                 print ("%3.2X"% ord (c) + "r 15s [NAK: DLE-START]")
  451.                 self.connectERR +=1 # Verbindungsaufbaufehler um 1 erhöhen             
  452.                 self.SetSendDelay (self.CWZ)                
  453.                 self.errNAK ()
  454.       
  455.  
  456.     # Schritt 4: Empfangen der Daten, Verbindungsaufbau
  457.     # Es wird das DLE gesendet f+r_ wir sind empfangsbereit
  458.     # Danach muss innerhalb der QVZ der Stream beginnen
  459.     def schritt_4 (self):
  460.         if self.newstep:
  461.             if self.CFG_PRINT:
  462.                 print (" 10s",end="")
  463.             self.RS232.flushOutput ()
  464.             self.RS232.flushInput ()
  465.             self.RS232.write (self.DLE)          
  466.         # Nach dem DLE muss nun innerhalt der ZVZ der Datenstream beginnen
  467.         if self.stepdauer>self.ZVZ:
  468.             # Zeichenverzugszeit ist abgelaufen NAK fehler
  469.             if self.CFG_PRINT:
  470.                 print (" 15s [NAK: ZVZ-START]")            
  471.             self.errNAK ()  
  472.         elif self.RS232.inWaiting ():
  473.             # Zeichen innerhalb der Zeit im Puffer, alles ist gut
  474.             self.setnewstep (5)  
  475.  
  476.     # Schritt 5: Empfangen Datenstream
  477.     # der Datenstream wird empfangen, bis der Parser die Sequenz DLE ETX erkennt
  478.     def schritt_5 (self):
  479.         # Wenn der Schritt neu aufgerufen wird, dann STX_EN auf False setzen
  480.         if self.newstep:
  481.             self.STX_EN  =False
  482.             self.BCC_EN  =False
  483.             self.readbuff=b""
  484.         # Abfrage der Zeichenverzugszeit
  485.         # Zeichenverzug ist aufgetreten (NAK wird gesendet)
  486.         # Empfangsfehler hochzählen
  487.         if t.time ()-self.starttime > self.ZVZ:
  488.             if self.CFG_PRINT:
  489.                 print (" 15s [NAK: ERR-ZVZ]")
  490.             self.errNAK ()
  491.         else:    
  492.             #solange wie zeichen im puffer oder EndeStream nicht erkannt    
  493.             while self.RS232.inWaiting():
  494.                 #zeichen aus dem puffer lesen
  495.                 c=self.RS232.read ()
  496.                 self.starttime=t.time () # Zeit setzen beim letzten Empfangenen Zeichen
  497.                 if self.CFG_PRINT:
  498.                     print("%3.2X"% ord (c) + "r",end="")
  499.                 self.readbuff=self.readbuff+c    
  500.                 if self.BCC_EN:
  501.                     rec=self.inframe (self.readbuff)
  502.                     if rec is None:
  503.                         # Fehler beim Zerlegen vom Inframe oder Checksum fehler
  504.                         if self.CFG_PRINT:
  505.                             print (" 15s [NAK: ERR-BCC]")
  506.                         self.errNAK ()
  507.                     else:    
  508.                         if self.CFG_PRINT:
  509.                             print (" 10s [DLE: OK]")
  510.                         self.ReadSuccess (rec)                               
  511.                         self.RS232.flushInput ()
  512.                         self.RS232.flushOutput ()                      
  513.                         t.sleep (self.SLP) # Für die erlaubte Quittungsverzugszeit legt sich der Prozess mal schlafen
  514.                         self.RS232.write (self.DLE)
  515.                     self.setnewstep (0)    
  516.                     break
  517.                 elif (c==self.DLE):
  518.                     self.STX_EN= not (self.STX_EN)
  519.                 elif ((c==self.ETX) and self.STX_EN):
  520.                     self.BCC_EN= True # Endekennung gültig erkannt, nun das BCC als letztes Zeichen
  521.                 else:
  522.                     self.STX_EN=False
  523.                     self.BCC_EN=False
  524.         return
  525.  
  526.     
  527.     # Wird immer ausgeführt vor den Schritten
  528.     def schritt (self):
  529.         options = {0 : self.schritt_0,1: self.schritt_1, 2: self.schritt_2, 3: self.schritt_3, 4: self.schritt_4, 5: self.schritt_5}
  530.         options [self.step]()
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.