Zustandsbits in Zustands-Integer

Mangokind

Level-1
Beiträge
73
Reaktionspunkte
9
Zuviel Werbung?
-> Hier kostenlos registrieren
ne eigentlich ganz simple aufgabe, die mich gerade überfordert:

der Zustand eines Maschinenablaufs/einer Arbeitsstation einer Maschine o.ä. kann wahlweise als Integer oder in einer Folge von Bits gespeichert werden.
dabei ist jedem Bit fest eine Zustandsnummer zugeordnet.

also als integer

Code:
Zst      INT     5

oder als Bits

Code:
Zst1     Bool     FALSE
Zst2     Bool     FALSE
Zst3     Bool     FALSE
Zst4     Bool     FALSE
Zst5     Bool     TRUE

ich will nun ein programm schreiben, was variante 2 in variante 1 überführt.
es soll auch möglich sein, dass eine gewisse anzahl bits/zustände gleichzeitig aktiv sind
wenn also 5 zustandsbits gleichzeitig aktiv sind, will ich deren zugeordneten zustand in 5 Ints abspeichern


mir fällt zu dieser simplen Aufgabe gerade nix besseres ein, als

Code:
//IN nach TEMP
      L     #QuellDB                    //IN-Var... DB-Nummer Quelle
      T     #QDB                        //TEMP-Var... DB-Nummer Quelle
      L     #ZielDB                     //IN-Var... DB-Nummer Ziel
      T     #ZDB                        //TEMP-Var... DB-Nummer Ziel

// --- initialisieren ---
//Pointer erstellen
      L     #QuellAnfang                //Byteposition erstes Bit
      SLD   3
      L     #HoechsterZustand           //Anzahl Bits
      +D                                //Pointer auf die Zustandsbits zeigt an
      T     #Bitpointer                 //dieser Stelle noch auf das letzte Bit
//Zaehler/Merker initialisieren
      L     0
      T     #GefundeneZst
 
// --- Pruefschleife ---
//geht Bit fuer Bit durch. sollte eins der Bits true sein, steht im Schleifenzaehler die Nummer des Zustands
      L     #HoechsterZustand           //Anzahl Bits
nw1b: T     #Schleifenzaehler
      AUF   DB [#QDB]
      LAR1  #Bitpointer
      SET   
      U      [AR1,P#0.0]
      SPBN  nw1c
//wenn das Bit, auf das Bitpointer gerade zeigt, gesetzt ist
//Zielposition im Ziel-DB laden
      AUF   DB [#ZDB]
      L     #GefundeneZst               //Anzahl gefundener Zustaende
      SLD                               //*2 um 2 Bytes bzw 1 Int weiterzuspringen
      L     #ZielAnfang                 //IN-Var... Byteposition des ersten Zst-Ints im Ziel-DB
      +D                                //im akku 1 steht die Byte-Nummer des Ints, in das der Zustand gespeichert wird
      SLD   3
      LAR1  
//gefundenen Zustand abspeichern
      L     #Schleifenzaehler
      T     W [AR1,P#0.0]
//Zaehler erhoehen
      L     #GefundeneZst
      +     1
      T     #GefundeneZst
//Abbruchbedingung: speicherplatz fuer Zustandsnummern ist voll
      L     #GefundeneZst
      L     #AnzahlInteger              //IN-Var... Vorgabe der maximal gleichzeitig gesetzten Zst bzw die Anzahl Speicherplaetze
      ==I   
      BEB   
nw1c: L     #Bitpointer                 //Pointer auf die Zustandsbits um 1 verringern
      +     -1
      T     #Bitpointer
      L     #Schleifenzaehler
      LOOP  nw1b

umständlich und verbrät vermutlich zuviel Rechenzeit durch die Loopschleife mit unzähligen Durchläufen

hat vielleicht jemand ne elegantere idee? :D
 
zur Verbesserung der Laufzeit auf Kosten von Speicherplatz und Lesbarkeit dasselbe nochmal mit 2 verschachtelten Schleifen.

die äußere Schleife prüft den Quellbereich WORD-weise auf gesetzte Bits und wenn mindestens eins gesetzt ist, Schaut die innere Schleife, welche Bits genau gesetzt sind...

Code:
//IN nach TEMP
      L     #QuellDB                    //IN-Var... DB-Nummer Quelle
      T     #QDB                        //TEMP-Var... DB-Nummer Quelle
      L     #ZielDB                     //IN-Var... DB-Nummer Ziel
      T     #ZDB                        //TEMP-Var... DB-Nummer Ziel
 
// --- initialisieren ---
//Zaehler/Merker initialisieren
      L     0
      T     #GefundeneZst
 
//Offset der aeusseren Schleife 0 setzen
      L     0
      T     #OffsetAussen
 
//Zst-Ints im Ziel-DB 0 setzen
      AUF   DB [#ZDB]
      L     #ZielAnfang                 //IN-Var... Byteposition des ersten Zst-Ints im Ziel-DB
      SLD   3
      LAR1  
      L     #AnzahlInteger
nw1a: T     #SchleifeInnen
      L     0
      T     W [AR1,P#0.0]
      +AR1  P#2.0
      L     #SchleifeInnen
      LOOP  nw1a
 
 
// --- Pruefschleifen ---
//*** Pruefschleife aussen: testet je 16 bits, ob überhaupt eins davon gesetzt ist ***
 
      L     #HoechsterZustand           //Anzahl Bits
      SRW   4
      +     1                           //Anzahl Woerter
nw1b: T     #SchleifeAussen
      AUF   DB [#QDB]
      L     #OffsetAussen               //Anzahl gepruefter Bytes
      L     #QuellAnfang
      +I    
      SLD   3
      T     #QPointer                   //Pointer auf aktuell zu testenden Quellbereich
      LAR1  
 
      L     W [AR1,P#0.0]
      L     0
      ==I   
      SPB   nw1e                        //wenn kein bit true ist, muss man auch nicht schauen, welches true ist
//***Innere Schleife***
      L     16
nw1c: T     #SchleifeInnen
      AUF   DB [#QDB]
      L     #QPointer
      L     #SchleifeInnen
      +D    
      +     -1                          //weil Bits von 0..15, aber SchleifeInnen von 1..16
      LAR1  
      SET   
      U      [AR1,P#0.0]
      SPBN  nw1d
//wenn das Bit, auf das Bitpointer gerade zeigt, gesetzt ist
//Zielposition im Ziel-DB laden
      AUF   DB [#ZDB]
      L     #GefundeneZst               //Anzahl gefundener Zustaende
      SLD                               //*2 um 2 Bytes bzw 1 Int weiterzuspringen
      L     #ZielAnfang                 //IN-Var... Byteposition des ersten Zst-Ints im Ziel-DB
      +D                                //im akku 1 steht die Byte-Nummer des Ints, in das der Zustand gespeichert wird
      SLD   3
      LAR1  
//gefundenen Zustand abspeichern
      L     #OffsetAussen               //Anzahl durchsuchter Bytes
      SLW   3                           //Anzahl durchsuchter Bits
      L     #SchleifeInnen              //Position innerhalb des aktuell durchsuchten Wortes
      +I                                //Nummer des dem Bit zugewiesenen Zustands
      T     W [AR1,P#0.0]
//Zaehler erhoehen
      L     #GefundeneZst
      +     1
      T     #GefundeneZst
//Abbruchbedingung: speicherplatz fuer Zustandsnummern ist voll
      L     #GefundeneZst
      L     #AnzahlInteger              //IN-Var... Vorgabe der maximal gleichzeitig gesetzten Zst bzw die Anzahl Speicherplaetze
      ==I   
      BEB   
//Ende innere Schleife
nw1d: L     #SchleifeInnen
      LOOP  nw1c
//Ende aeussere Schleife
nw1e: L     #OffsetAussen
     + 2  
      T     #OffsetAussen               //Anzahl durchsuchter Bytes um 2 erhoehen
      L     #SchleifeAussen
      LOOP  nw1b

elegant ist aber wirklich was anderes... ich wäre für Anregungen dankbar
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Das einzige, was mir auf Anhieb einfällt, wäre, nicht jedesmal die komplette Adresse neu zu berechnen. Statt dieser Neuberechnung in der inneren Schleife könntest du einfach auf den Pointer, der auf das Bit zeigt P#0.1 aufaddieren.
 
aber der Wert im AR1 könnte sich ändern, falls ein gesetztes Zustandsbit gefunden wurde

dann würde ja ein Ziel-DB geöffnet und irgendwo in den hineingeschrieben, wobei AR1 überschrieben würde...

ich könnte natürlich auch für das eine AR1 nehmen und für das andere AR2...

gute idee... danke dir! :)
 
aber der Wert im AR1 könnte sich ändern, falls ein gesetztes Zustandsbit gefunden wurde

dann würde ja ein Ziel-DB geöffnet und irgendwo in den hineingeschrieben, wobei AR1 überschrieben würde...

ich könnte natürlich auch für das eine AR1 nehmen und für das andere AR2...

gute idee... danke dir! :)

Du berechnest einmal den Pointer beim Eintritt in die Schleife. Der steht ja in einer Variable. Zu dieser einfach P#0.1 dazuaddieren, ins AR1 laden, fertig. Den Pointer würde ich schon nutzen, AR2 mag ich eher nicht so, wegen der FB- und Multiinstanzen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
das wird ja doch schwieriger als angenommen, da die loopschleife runterzaehlt, also vom letzten zum ersten bit vorgeht, ich aber das adressregister nicht um 1 verringern kann :D

bzw ich müsste AR1 nach akku 1 laden, um 1 verringern und akku 1 zurueck nach AR1

da wäre aber dann jede ersparnis hinüber


oder ich versteh dich total falsch :D
 
Zuletzt bearbeitet:
das wird ja doch schwieriger als angenommen, da die loopschleife runterzaehlt, also vom letzten zum ersten bit vorgeht, ich aber das adressregister nicht um 1 verringern kann :D

Probiers einfach mal, wir hatten das schon mal diskutiert. Wenn ich mich recht erinnere, geht es sagar, man kann P#0.1 auch abziehen.
Andererseits kann die Loopschleife ja ruhig runterzählen und der Pointer erhöht werden, kommt ja nur auf die richtige Startadresse an.

PS: Ich glaube, du hast Recht, wirklich viel bringt das nicht. So wie du das geschrieben hast findet man sich wenigstens zurecht, das ist ohnehin wichtiger, als vielleicht eine Operation einzusparen.
 
Zuletzt bearbeitet:
Kleiner Tipp anderer Natur:

Wenn Du die IN-Variablen der DBs als "Block-DB" ausführst brauchst Du die TEMP_VAR nicht mehr, sondern

1. kannst die Block_Dbs direkt mit AUF aufrufen

und
2. findest Du die DBs wieder in den Querverweisen...

dtsclipper
 
Zuviel Werbung?
-> Hier kostenlos registrieren
ich glaube, dass mein ansatz insgesamt viel zu umständlich ist.

rein mathematisch müsste ich mir nur den exponenten zur basis 2 ausgeben lassen, aber ein kurzer blick ins S7 PDF mit der ausführungszeit für logarithmen hat mich ganz schnell wieder von dieser idee abgebracht

würde den code zwar zu nem 10-zeiler vereinfachen, aber die laufzeit...:rolleyes:

Kleiner Tipp anderer Natur:

Wenn Du die IN-Variablen der DBs als "Block-DB" ausführst brauchst Du die TEMP_VAR nicht mehr, sondern

1. kannst die Block_Dbs direkt mit AUF aufrufen

und
2. findest Du die DBs wieder in den Querverweisen...

dtsclipper

danke dir! aber ich kann keine Parametertypen in DBs speichern... zumindest nicht bei AWL und der Aufruf akzeptiert dann keine Zahlenwerte mehr
d.h. das Problem mit den TEMP-Vars würde sich in die aufrufende Funktion verlagern
 
Zuletzt bearbeitet:
Ich bin einfach davon ausgegangen das die Dbs schon Existieren...
Sollen die erst mit dem SFC82 erzeugt werden da die Nummer nicht fix ist ? :confused:

dtsclipper
 
Zuviel Werbung?
-> Hier kostenlos registrieren
das man Parametertypen nicht in DBs speichern kann, ist einfach so... ich kann auch keine ANY-pointer oder sowas in DBs speichern... (oder geht sowas mit SCL?)

und wenn ich als IN-parameter BlockDB angebe, kann ich beim CALL aufruf nicht mehr einfach an die Varbiable die Zahl dranschreiben.


also wenn ich als Quell-DB DB5 nehmen will und die Funktion einen BlockDB als Quelle erwartet, kann ich beim CALL bei der variable nicht einfach = 5 hinschreiben

ich müsste also 5 in einer TEMP-variable vom typ block-DB speichern und diese dann beim Aufruf übergeben.

Code:
      CALL  FC    XY
       BlockDBIn:=5

erzeugt bei mir zumindest den fehler
konstantenformat zu datentyp int passt nicht zu formalem typ block_db des formalparameters BlockDBIn

ich finde das für die Wiederverwendbarkeit ungünstiger...

wobei das natürlich seinen charme hätte, wenns dann bei den Querverweisen auftaucht...
 
Du musst beim Block_DB den DB eintragen...

sieht so aus:
Code:
      CALL  FB    22 , DB25
       Stoermelde_DB   :=DB20
       Spiegel_DB      :=DB24
       Zaehl_DB        :=DB21
       Daten_DB        :=DB31
       Max_Anzahl_Bytes:=175

... dtsclipper
 
ne schöne lösung, aber ich darf gerade ein programm schreiben, was ganz grob folgende aufgabe hat:

eine maschine hat mehrere arbeitsstationen

jeder arbeitsstation ist jeweils 1 FC und 1 DB für ihren ablauf zugeordnet

innerhalb des DBs steht irgendwo entweder als integer oder in mehreren bits der aktuelle zustand der arbeitsstation/des ablaufs

und das programm schaut sich jeden ablauf bzw jedes ablauf-DB nacheinander an, wertet den zustand des ablaufs aus und behandelt dann den ablauf entsprechend gewisser parameter

das programm soll auf möglichst vielen maschinen von möglichst vielen herstellern mit jeweils eigener programmstruktur und pflichtenheft lauffähig sein, ohne - abgesehen vom setzen der parameter - nennenswert verändert zu werden.

daneben gibts noch die lustige forderung, dass alle DB-nummern recht problemlos angepasst werden können müssen.

heißt, jemand benennt den DB80 in DB90 um und sofern er in den kopfzeilen aller anderen FCs die nummer auch noch ändert, soll das programm immernoch gehen.

kurzum: es gibt im gesamten programm nicht eine einzige direkte adressierung (bei 35KB code) ausgenommen FC-aufrufe, weil man da mit dem parameter übergeben probleme bekommt

ist also insgesamt sehr sehr allgemein gehalten und bedarf ner unmenge an einstellungen...

ich kann also die im hiesigen problem genutzten Quell-(bzw ablauf-)DBs nicht absolut übergeben, sondern muss deren nummer aus irgendwelchen Arrays auslesen, weil die Anzahl und nummern der ABlauf-DBs von hersteller zu hersteller schwanken...

ein spaß...

irgendwie sind mir da integer lieber als BLOCK_DB

damit kann ich umgehen :D

aber danke trotzdem für den tipp
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Logarithmus

Hi, die Idee mit dem Logarithmus (ein paar Posts vorher) habe hab ich schon mal in SCL gemacht. Die Funktion ist allerdings nur für ein Wort, kann aber problemlos erweitert werden.

FUNCTION FC202 : INT
VAR_INPUT
m_word : INT; // zu prüfendes Wort
END_VAR
VAR_TEMP
i_temp : INT; // temporäre Variable
END_VAR
IF m_word = 0 THEN
i_temp := 0;
ELSIF m_word = -32768 THEN
// Anweisungsteil_ELSIF
i_temp := 16;
ELSE
i_temp := 1 + REAL_TO_INT (ROUND (LN (INT_TO_REAL (m_word)) / LN (2.000000e+000)));
END_IF;
FC202 := i_temp;
END_FUNCTION

Gruß
Stefan
 
Hallo,
wie wäre es denn dann mit :
Code:
FUNCTION FC202 : INT
VAR_INPUT
m_word : INT; // zu prüfendes Wort
END_VAR
VAR_TEMP
i_temp : INT; // temporäre Variable
END_VAR
IF m_word = 0 THEN
i_temp := 0; 
ELSIF m_word = -32768 THEN
// Anweisungsteil_ELSIF
i_temp := 16;
ELSE
[B][COLOR=green]i_temp := 2**(m_word -1) ;[/COLOR][/B]
END_IF;
FC202 := i_temp;
END_FUNCTION
Gruß
LL
 
Zuletzt bearbeitet:
Zurück
Oben