Step 7 Guter Programmierstil?

truth

Level-1
Beiträge
7
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,
um einen übersichtlichen (gut lesbaren) Quellcode zu erhalten, habe ich meinen AWL-Code als Zustandsautomaten programmiert.
Sobald ich jedoch Timer verwende wird es schwierig.
Mein erster Versuch (siehe Beispiel A) hat dann auch promt nicht funktioniert, weil der Timer so die steigenden und fallenden Flanken des Eingangssignals (#event bzw. #reset) nicht mehr mitbekommt.
Code:
[FONT=Courier 10 Pitch]// Example A:[/FONT]
 [FONT=Courier 10 Pitch]        L    #state[/FONT]
 [FONT=Courier 10 Pitch]        SPL  d[/FONT][FONT=Courier 10 Pitch]ef[/FONT][FONT=Courier 10 Pitch]           //    switch([/FONT][COLOR=#000000][FONT=Courier 10 Pitch]state[/FONT][/COLOR][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA  [/FONT][FONT=Courier 10 Pitch]z[/FONT][FONT=Courier 10 Pitch]0            //    case: 0 ([/FONT][FONT=Courier 10 Pitch]waiting for event[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA  [/FONT][FONT=Courier 10 Pitch]z1[/FONT][FONT=Courier 10 Pitch]            //    case: [/FONT][FONT=Courier 10 Pitch]1[/FONT][FONT=Courier 10 Pitch] ([/FONT][FONT=Courier 10 Pitch]timer started[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA  [/FONT][FONT=Courier 10 Pitch]z2[/FONT][FONT=Courier 10 Pitch]            //    case: [/FONT][FONT=Courier 10 Pitch]2[/FONT][FONT=Courier 10 Pitch] ([/FONT][FONT=Courier 10 Pitch]waiting for [/FONT][FONT=Courier 10 Pitch]reset[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]def:    SPA  err           //    default:[/FONT]
 [FONT=Courier 10 Pitch]err:    NOP  0             //    Zustand invalid[/FONT]
 [FONT=Courier 10 Pitch]        SPA     end[/FONT]

 [FONT=Courier 10 Pitch]z0:     U    #event        //    [/FONT][FONT=Courier 10 Pitch]if(#event)[/FONT]
 [FONT=Courier 10 Pitch]        L    ST5#10s        [/FONT]
 [FONT=Courier 10 Pitch]        SS   T1            //    [/FONT][FONT=Courier 10 Pitch]start timer[/FONT]
 [FONT=Courier 10 Pitch]        L    1[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=1[/FONT]
 [FONT=Courier 10 Pitch]        SPA  end:[/FONT]

 [FONT=Courier 10 Pitch]z1:     [/FONT][FONT=Courier 10 Pitch]U    T1[/FONT]
 [FONT=Courier 10 Pitch]        =    #output[/FONT][FONT=Courier 10 Pitch]       //    … [/FONT][FONT=Courier 10 Pitch]doing something[/FONT]
 [FONT=Courier 10 Pitch]        L    [/FONT][FONT=Courier 10 Pitch]2[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=2[/FONT]
 [FONT=Courier 10 Pitch]        SPA  end:    [/FONT]

 [FONT=Courier 10 Pitch]z2:     U    #reset        //    if(#reset)[/FONT]
         [FONT=Courier 10 Pitch]R    T1[/FONT]
 [FONT=Courier 10 Pitch]        L    0[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=0[/FONT]
  
 [FONT=Courier 10 Pitch]end:    BEA[/FONT]

Eine Lösung ist, die Timer aus der Zustandsmaschine wieder herrauszunehmen (siehe Beispiel B), ...
Code:
[FONT=Courier 10 Pitch]// Example B:[/FONT]
         [FONT=Courier 10 Pitch]L    #state[/FONT]
 [FONT=Courier 10 Pitch]        SPL    d[/FONT][FONT=Courier 10 Pitch]ef[/FONT][FONT=Courier 10 Pitch]         //    switch([/FONT][COLOR=#000000][FONT=Courier 10 Pitch]state[/FONT][/COLOR][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA    [/FONT][FONT=Courier 10 Pitch]z[/FONT][FONT=Courier 10 Pitch]0          //    case: 0 ([/FONT][FONT=Courier 10 Pitch]waiting for event[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA    [/FONT][FONT=Courier 10 Pitch]z1[/FONT][FONT=Courier 10 Pitch]          //    case: [/FONT][FONT=Courier 10 Pitch]1[/FONT][FONT=Courier 10 Pitch] ([/FONT][FONT=Courier 10 Pitch]timer started[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]        SPA    [/FONT][FONT=Courier 10 Pitch]z2[/FONT][FONT=Courier 10 Pitch]          //    case: [/FONT][FONT=Courier 10 Pitch]2[/FONT][FONT=Courier 10 Pitch] ([/FONT][FONT=Courier 10 Pitch]waiting for [/FONT][FONT=Courier 10 Pitch]reset[/FONT][FONT=Courier 10 Pitch])[/FONT]
 [FONT=Courier 10 Pitch]def:    SPA err            //    default:[/FONT]
 [FONT=Courier 10 Pitch]err:    NOP 0              //    Zustand invalid[/FONT]
         [FONT=Courier 10 Pitch]SPA     end[/FONT]

 [FONT=Courier 10 Pitch]z0:     U    #event        //    [/FONT][FONT=Courier 10 Pitch]if(#event)[/FONT]
 [FONT=Courier 10 Pitch]        S    #start_timer[/FONT]
 [FONT=Courier 10 Pitch]        L    1[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=1[/FONT]
 [FONT=Courier 10 Pitch]        SPA    end:[/FONT]
  
 [FONT=Courier 10 Pitch]z1:     [/FONT][FONT=Courier 10 Pitch]U    T1[/FONT]
 [FONT=Courier 10 Pitch]        =    #output[/FONT][FONT=Courier 10 Pitch]       //    … [/FONT][FONT=Courier 10 Pitch]doing something[/FONT]
 [FONT=Courier 10 Pitch]        L    [/FONT][FONT=Courier 10 Pitch]2[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=2[/FONT]
 [FONT=Courier 10 Pitch]        SPA    end:    [/FONT]

 [FONT=Courier 10 Pitch]z2:     U    #reset        //    if(#reset)[/FONT]
 [FONT=Courier 10 Pitch]        S    #reset_timer[/FONT]
 [FONT=Courier 10 Pitch]        L    0[/FONT]
 [FONT=Courier 10 Pitch]        T    #state        //    [/FONT][FONT=Courier 10 Pitch]switch to state=0[/FONT]
  
 [FONT=Courier 10 Pitch]end:    U    #start_timer[/FONT]
 [FONT=Courier 10 Pitch]        L    ST5#10s        [/FONT]
 [FONT=Courier 10 Pitch]        SS    T1[/FONT]
 [FONT=Courier 10 Pitch]        R    #start_timer[/FONT]
  
 [FONT=Courier 10 Pitch]        U    #reset_timer[/FONT]
         [FONT=Courier 10 Pitch]R    T1[/FONT]
 [FONT=Courier 10 Pitch]        R    #reset_timer[/FONT]
 [FONT=Courier 10 Pitch]        BEA[/FONT]

... was jedoch zur Folge hat, dass die Lesbarzeit des Programms leidet (insbesondere wenn man viele Timer benutzen will/braucht).

FRAGE:
Ist es überhaupt sinnvoll einen Zustandautomaten und Timer gleichzeitig zu verwenden, oder ist das schlechter Programmierstil?
Wie sonst könnte man dieses Problem lösen, damit auch große Programme noch gut lesbar bleiben?

Viele Grüße,
Truth
 
Hallo,
so wie ich das sehe läuft dein Zustandsautomat (Schrittkette) unabhängig vom Timer durch.
Bedenke immer : Lade und Transfer-Operationen sind von vorgeschalteten Verknüpfungen unabhängig und werden immer und absolut ausgeführt.
Ich würde eine Schrittkette so gar nicht erstellen.
Benutz doch zu dem Thema mal die Forums-Suche. Das bringt dir massenhaft Beispiele ...

Gruß
Larry
 
Upps,
... ich hab die 'bedingten Sprunganweisungen' vergessen.
Hier also das überarbeite Beispiel B2:
Code:
// Example B2:
         L    #state
         SPL  def           //    switch(state)
         SPA  z0            //    case: 0 (waiting for event)
         SPA  z1            //    case: 1 (timer started)
         SPA  z2            //    case: 2 (waiting for reset)
 def:    SPA  err           //    default:
 err:    NOP  0             //    Zustand invalid
         SPA  end

 z0:     U    #event       
         SPBN end
         S    #start_timer  //    if(#event)
         L    1
         T    #state        //    switch to state=1
         SPA  end
  
 z1:     U    T1
         SPBN end
         =    #output       //    … doing something
         L    2
         T    #state        //    switch to state=2
         SPA  end    

 z2:     U    #reset       
         SPBN end
         S    #reset_timer  //    if(#reset)
         L    0
         T    #state        //    switch to state=0
  
 end:    U    #start_timer
         L    ST5#10s        
         SS   T1
         R    #start_timer  
         U    #reset_timer
         R    T1
         R    #reset_timer
         BEA

FRAGE:
Wie könnte ich denn den Zustandautomat ohne Spungverteiler realisieren?

Grüße Truth
 
Du könntest noch einen Schritt einfügen, so dass der Timer erstmal resettet wird (und false sieht).

Da ich davon ausgehe, dass T1 true ist, so lange die Zeit läuft (habe grade nichts zum nachlesen da) habe ich im Schritt 2 habe ich die Aktion vor die Weiterschaltbedingung gezogen und diese auch in SPB geändert. Damit ist #output true, solange der Timer läuft.

Das SPA end am Ende der Schritte könnte man hier auch weglassen. Ich vermute das NOT und SS T1 im Schritt 0 ist auch nicht wirklich notwendig, konnte ich aber nicht ausprobieren.

Code:
         L    #state
         SPL  err          //    switch(state)
         SPA  z0            //
         SPA  z1            //    case: 1 (waiting for event)
         SPA  z2            //    case: 2 (timer started)
         SPA  z3            //    case: 3 (waiting for reset)

 err:   CALL SFC46         //     state invalid: Stop CPU

 z0:    SET 
        R T1               //    reset T1
        NOT
        SS   T1            //    make shure T1 sees false
        L    1
        T    #state        //    switch to state=1
        SPA  end
		

 z1:    U    #event       
        SPBN end
        L    ST5#10s       //    if(#event)   
        SS   T1            
        L    2
        T    #state        //    switch to state=2
        SPA  end
  
 z2:    U    T1
        =    #output       //    … doing something
        SPB end
        L    3
        T    #state        //    switch to state=3
        SPA  end    

 z3:    U    #reset       
        SPBN end
        S    #reset_timer  //    if(#reset)
        L    0
        T    #state        //    switch to state=0
  
end:    BEA


Und noch besser Programmierstil wäre es nicht fest T1 zu verwenden, sondern TON oder TOF (Timerbausteine aus der IEC Bib)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was mir überhaupt nicht einleuchtet - Warum soll der Code mit dem Sprungverteiler lesbarer sein als ohne?
Code:
[FONT=Courier New]         U    #event        //    if(#event)
         L    ST5#10s        
         SS   T1            //    start timer
         
 
         U    T1
         =    #output       //    … doing something

          
         U    #reset        //    if(#reset)
         R    T1[/FONT]
Das kann ich tausendmal schneller erfassen, als das ganze Gedöns mit dem Sprungverteiler.

Und besserer Programmierstil ist es m.M.n. auch noch, denn er benötigt keine unnötigen Sprünge!
In jeder anderen Sprache gibt's was auf die Mütze, wenn man überhaupt mit GOTO arbeitet. :sm10:
Und nebenbei erledigt es auch noch das Problem mit dem Timer.
 
Guter Programmierstil ist - meines Erachtens - Schrittketten in S7Graph zu erstellen :)

@Hucki
Bitte nicht wieder einen Flamewar um's GOTO beginnen :ROFLMAO:

Gruß
Dieter
 
Warum S7Graph ?? Aus Gewohnheit oder gibt es gewichtige Argumente. Geht das nicht auf die Zykluszeit ?
Das gewichtigste:
Es ist graphisch, und aktive Schritte sind relativ leicht zu erfassen.
Zykluszeit ist jetzt kein gewichtiges Problem, da das Endergebnis (MC7) im Enteffekt ein Sprungsammelsurium ist.

Das für mich nachteiligste:
Die Art und Weise, wie die Schnittstelle von Graph funktioniert muss man irgendwie mögen ... was auf mich persönlich nicht zutrifft.
 
Also wenn Ich früher Schrittketten in AWL erstellt habe, hab Ich das auch immer mit SPL gemacht, das ganze am besten in einem FB, dann TON/TOFF als multiinstanzen verwendet und immer am Ende der kette alle aufgerufen. In der kette hab Ich dann nur IN und OUT von den multiinstanzen verschaltet!
Ich hab immer noch am anfang des Bausteines alle Signale welche irgendwo in einem Schritt gesetzt werden zurückgesetzt, und immer im jeweiligen Schritt wieder gesetzt, dadurch konnte Ich auch die Schrittnumervariable auf einen bestimmten Schritt ansteuern und die gesetzten bits waren dann trotzdem immer richtig!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich würde nur gerne verstehen, warum jemand der Meinung ist, ein Programm mit Sprungliste wäre lesbarer als ohne.
Ich bin bei Dir , das in diesem Beispiel die Lösung ohne Schrittketten besser ist.
Bei komplexeren Programmen erhöhen sie aber, in der Regel, die Übersicht und verbessern die Fehlersuche, da man sich immer nur um einen definierten Bereich des Programms kümmen muss.
 
Vielleicht noch einmal eine Anmerkung dazu :
Es gibt mehrere Möglichkeiten, eine Schrittkette zu realisieren. Eine Sprunglsiete zu verwenden ist nur eine davon - und wird nicht unbedingt von jedem favourisiert ...

Gruß
Larry
 
Vielen Dank für die vielen Rückmeldungen.

Zunächst möchte ich noch ein paar erklärende Worte zum Hintergrund meines Themas/Frage nachreichen:
Da ich sonst relativ viel in C/C++ programmiere, erschienen mir Sprungverteiler wie switch(...) { case ... } oder auch if(...){...}else{...} immer gut geeignet, um ein Programm übersichtlich zu gestalten. Außerdem habe ich früher mal Assembler (6502) programmiert, weshalb ich mir dachte, dass es nicht allzuschwierig sein sollte, jetzt mit AWL anzufangen.
Dass das ein Trugschluss war, ist mir klargeworden, als ich versucht habe, einen Timer in mein Programm zu integrieren.

Mein Problem ist, dass sich der Programmablauf in meinen bisherigen Programmen meistens an Zuständen orientiert hat, während die AWL-Programmierung wohl eher auf Zustandswechsel aufbaut.
Dass typische AWL-Programme sich von Assembler unterscheiden, ist mir ja schon aufgefallen ...
... aber dass z.B. in Schrittketten für jeden Schritt eine eigene Variable (true or false) definiert wird, nur um festzustellen, in wechem Schritt man sich befindet,
war mir bisher suspekt. Das war auch der Grund, warum ich mein Beispielprogramm B2 als Zustandsautomat erstellt habe.

Zurück zu meiner Ursprünglichen Frage:
Welches Konzept bzw. welcher AWL-Programmierstiel ist geeignet, um auch größere, komplexere Anwendungen realisieren zu können, ohne all zu schnell den Überblick zu verlieren?
Vielleicht hat auch jemand eine Idee, wo ich AWL-Beispieleprogramme (ab 40 Zeilen aufwärts) finde, die nicht nur erklären, was das Programm tut, sondern auch warum das Programm so und nicht anders geschrieben wurde.

Nochmals vielen Dank für alle bisheriegen Antworten.

Viele Grüße,
Truth
 
Das für mich nachteiligste:
Die Art und Weise, wie die Schnittstelle von Graph funktioniert muss man irgendwie mögen ... was auf mich persönlich nicht zutrifft.

In den Einstellungen kannst du "Benutzdefineirt" wählen und dir die Schnittstelle gestalten wie sie dir gefällt.
Im einfachsten Fall nimmst du Init_SQ und Automatik.

Wenn man sich aber den kompletten Parameter-Satz anschaut, dann fragt man sich schon ob die Entwickler noch von dieser Welt sind.
Allerdings gibt es auf der anderen Seite auch Anwender, die alles mit Graph lösen (Betriebsarten, Einrichtfunktionen, Fehlerdiagnose und vielleicht auch noch Kaffekochen und Frau beglücken)

Gruß
Dieter
 
Vielen Dank für die vielen Rückmeldungen.

Zunächst möchte ich noch ein paar erklärende Worte zum Hintergrund meines Themas/Frage nachreichen:
Da ich sonst relativ viel in C/C++ programmiere, erschienen mir Sprungverteiler wie switch(...) { case ... } oder auch if(...){...}else{...} immer gut geeignet, um ein Programm übersichtlich zu gestalten. Außerdem habe ich früher mal Assembler (6502) programmiert, weshalb ich mir dachte, dass es nicht allzuschwierig sein sollte, jetzt mit AWL anzufangen.
Dass das ein Trugschluss war, ist mir klargeworden, als ich versucht habe, einen Timer in mein Programm zu integrieren.
Warum programmierst Du dann nicht in SCL?

Das orientiert sich doch an Hochsprachen und sollte Dir somit wesentlich einfacher fallen. Und außerdem scheint ja Siemens selber von AWL auf SCL umzustellen. Die S7-1200 kann z.B. nur noch FUP/KOP oder SCL, aber kein AWL mehr.
 
Zurück
Oben