Step 7 DB Datenänderung überwachen

klauserl

Level-2
Beiträge
253
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo
Ist es möglich bei einem Datenbaustein eine Datenänderung zu überwachen?
Habe einen DB mit 20 Byte Länge, und möchte so ca. alle 10s eine Abfrage starten, ob sich Daten in diesem Bereich geändert haben.
Danke
 
Natürlich ist das möglich. Mach einen zweiten Baustein mit derselben länge. Vergleiche Byte für byte vom originalen zur kopie. wenn unterschied dann haben sich die daten geändert, nach dem vergleich blockmove den originalen inhalt in den kopierten DB.

mfG René
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hier die AWL Quelle einer Funktion, die das paramtrierbar erledigt

Code:
FUNCTION "m7b_DB_COMPARE_ByRef" : VOID
TITLE =DB_COMPARE_ByRef
//Vergleicht Anzahl Bytes von 2 Datenbausteinen
//Die Vergleichsroutine arbeitet sehr effektiv, da die Werte nicht als 8-Bit 
//sondern als 32-Bit-Werte verglichen werden. Weiterhin müssen die DBs nur einmal 
//geöffnet werden, dies erreicht man, indem man einen DB als Global-DB und den 
//anderen als Instanz-DB öffnet.
//
//AUTOR: S. Maag
//DATUM: 10/2010
//
//AENDERUNGSVERMERKE:
//--------------------------------------------------------------------------------
//DATUM        NAME        AENDERUNG
//--------------------------------------------------------------------------------
//19.10.2010   S.Maag      Basiert auf FC2 CopyDBW_ByRef
//25.10.2010   S.Maag      Wenn keine Einzelbytes mehr zu kopieren sind, dann
//                         darf man nicht zu END springen sondern zu EQ (Daten 
//                         gleich)
//--------------------------------------------------------------------------------
//
//HINWEISE:
AUTHOR : 'S.Maag'
FAMILY : Maagic7
VERSION : 0.1


VAR_INPUT
  SOURCE : BLOCK_DB ;    
  DEST : BLOCK_DB ;    
  SOURCE_BYTE : INT ;    
  DEST_Byte : INT ;    
  NoOfBytes : INT ;    
END_VAR
VAR_TEMP
  QUELL_ADR : DINT ;    
  ZIEL_ADR : DINT ;    
  AR1 : DINT ;    
  LOOPS : INT ;    
END_VAR
BEGIN
NETWORK
TITLE =Register sichern
//Adressregister 2 Global- und Instanz DB werden bei CALL-Aufrufen automatisch 
//vom System gesichert, bei UC nicht!
      TAR1  #AR1; // Adressregister 1 sichern

NETWORK
TITLE =Adressen für Quell und Ziel-DW als Pointer in Adressregister

      L     #SOURCE_BYTE; // Quelldatenbyte Nr.
      SLD   3; // als Pointer
      L     P#0.0; // 0-Pointer addieren, damit
      +D    ; // Kennungsbits für Pointer gesetzt werden
      LAR1  ; // ins Adressregister 1

      L     #DEST_Byte; // ZieldatenByte-Nr
      SLD   3; // als Pointer 
      L     P#0.0; 
      +D    ; 
      LAR2  ; // ins Adressregister 2
NETWORK
TITLE =Daten vergleichen

      AUF   #SOURCE; // Quell-DB als Global-DB öffnen
      TDB   ; // Global und Instanz-DB tauschen (Quelle wird Instanz-DB)
      AUF   #DEST; // Ziel-DB als Global-DB öffnen

      L     #NoOfBytes; // Anzahl der zu kopierenden Bytes
      L     4; // in Anzahl der 32-Bit-Werte umrechen
      /I    ; 
      SPZ   LAST; // Wenn 0 32-Bit Werte, dann weiter mit letzten 3 Bytes 
LOOP: T     #LOOPS; 
      L     DID [AR1,P#0.0]; // QuellDaten lesen
      L     DBD [AR2,P#0.0]; // VergleichsDaten lesen
      <>D   ; // wenn nicht gleich, dann Ende
      SPB   NEQ; 
      +AR1  P#4.0; // Quell-Pointer in AR1 um 4 Bytes erhöhen
      +AR2  P#4.0; // Ziel-Pointer in AR2 um 4 Bytes erhöhen
      L     #LOOPS; // Schleifenzähler wieder in Akku 1 zurück
      LOOP  LOOP; // Schleifenzähler bearbeiten

LAST: L     #NoOfBytes; // nun noch die evtl. letzte 3 Bytes prüfen
      L     2#11; // Anzahl übriger Bytes maskieren
      UW    ; // Anzahl übriger Bytes
      SPZ   EQ; // Wenn 0 dann ENDE
LOP1: T     #LOOPS; 
      L     DIB [AR1,P#0.0]; // QuellDaten lesen
      L     DBB [AR2,P#0.0]; // VergleichsDaten lesen
      <>I   ; // wenn nicht gleich, dann Ende
      SPB   NEQ; 
      +AR1  P#1.0; // Quell-Pointer in AR1 um 1 Byte erhöhen
      +AR2  P#1.0; // Vergleichs-Pointer in AR2 um 1 Byte erhöhen
      L     #LOOPS; // Schleifenzähler wieder in Akku 1 zurück
      LOOP  LOP1; // Schleifenzähler bearbeiten

EQ:   SET   ; // Daten sind gleich  (equal)
      SAVE  ; // ENO=TRUE
      CLR   ; 
      SPA   END; 

NEQ:  CLR   ; // Daten sind nicht identisch (not equal)
      SAVE  ; // ENO=FALSE

NETWORK
TITLE =Adressregister 1 wieder herstellen

END:  LAR1  #AR1; // Adressregister 1 wieder herstellen

END_FUNCTION
 
Hab hier nochwas, was detialierter prüft und für jeden Wert ein Equal-Bit zurückschreibt.
Das muss man nicht 2x erfinden.

Vor Feldeinsatz aber bitte nochmal testen! Ich hab das zwar getestet aber noch nicht im aktiven Einsatz!
Falls das jemand testet, bitte Info zurück!
Danke

Code:
FUNCTION m7_BlockCompareDWORD : BOOL

    TITLE=    'BlockCompareDWORD' 
    VERSION:  '1.0'
    AUTHOR:   'S.Maag'   
    FAMILY:   'Maagic7'     

(*  ================================================================================
     BlockCompareDWORD vergleicht 2 Datenblöcke DWORD-weise     
     die Daten können beliebiger Natur sein. Sinn machen jeodoch
     Felder von DINT, DWORD, REAL (z.B. ARRAYs)
     
     Das Vergleichsergebnis je DWORD wird in einem Bit gespeichert
     und in DB blkDB_BOOL übertragen. Für die beiden Datenblöcke können
     die DB's und eine Sartadress in Byte angeben werden. 
     
     Die DB-Grenzen werden automatisch geprüft. Ist der DB kleiner
     als durch ByteAdr + NoOfDWORDs * 4, dann werden nur soviele
     DWORD geprüft wie möglich.
           
     ANWENDUNG:
     Prüfen ganzer Parameterlisten aus REAL, DINT, DWORD Werten
     auf einmal. Die Vergleichsergebnisse werden dabei in einen 
     Ziel-DB aus BOOL geschrieben. 
     Anwendungsbeispiele:
      - detailierter Vergleich von User-Parametern und Werkseinstellungen
      - detailierter Vergleich von Rezeptdaten zweier Rezepte
      
      Durch die Bitweise Speicherung der Vergleichserbnisse kann
      die BOOLsche Liste gleich für WinCC hergenommen werden, um 
      die unterschiedlichen Werte am Bildschirm farblich hervorzuheben.
      Die BOOLs liest man in WinCC am besten als ARRAY ein.
      Bei Rezeptdaten kann man so z.B. darstellen, welcher Wert des
      aktuelle geladenen Rezepts durch den User verändert wurde.
    
    AUTHOR: S.Maag
    DATE: 14.07.2017
       
    CHANGELOG:
    --------------------------------------------------------------------------------
    DATE        NAME        DESCRIPTION
    --------------------------------------------------------------------------------
      
    --------------------------------------------------------------------------------
    *)

VAR_INPUT
    NoOf_DWORDs : INT;      // No of DWORDs to compare
    
    blkDB1 : BLOCK_DB;      // first DataBlock
    DB1_ByteAdr : INT;      // ByteStartAdress of our DATA in first DataBlock
    
    blkDB2 : BLOCK_DB;      // second DataBlock
    DB2_ByteAdr : INT;      // ByteStartAdress of our DATA in first DataBlock

    blkDB_BOOL : BLOCK_DB;  // DataBlock to store the compare result bits
    DB_BOOL_ByteAdr : INT;  // ByteStartAdress of result bits
END_VAR

VAR_OUTPUT
    // This values should be == NoOf_DWORDs from VAR_INPUT
    // if there is a difference, the BLOCK size doesn't cpompare!
    // in this case DWORDs will be compared up END of shortes BLOCK
    dNoOfComparedDwords : DINT;         // total No of compared DWORDs
    
    // This values should be == NoOf_DWORDs from VAR_INPUT
    // if lower, the BIT-BUFFER to store the compare resultus is smaller than
    // the No of DWORDs to compare
    dNoOfStoredResultDwords : DINT;     // total No of compared DWORDs stored in Result-Bit (blkDB_BOOL)
    
    dNoOfEqualDwords : DINT;    // The No of compared DWORDs which are equal
END_VAR

VAR_TEMP

    L_DB1 : DINT;       // Length in bytes of BLOCK_DB1
    L_DB2 : DINT;       // Length in bytesof BLOCK_DB2
    L_DB_BOOL : DINT;   // Length in bytes of BLOCK_DB_BOOL (destination BLOCK for compare result)
    
    wBYTEs : WORD;      // Byte length of DB, returned by SFC_24 TEST_DB
    wDBNo : WORD;       // DBNo of BLOCK-DB, Input value for SFC_24 TEST_DB
    RET:INT;            // Return value for SFCs

    idxDB1 : DINT;      // Byte-Index of blkDB1, used to read from blkDB1.DD[idxDB1] 
    idxDB2 : DINT;      // Byte-Index of blkDB2
    idxDB_BOOL : DINT;  // Byte-Index of blkDB_BOOL

    nDWORDs : DINT;     // possible No of DWORDs to compare if blkDB1 and blkDB2 are of same size nDWORDs == VAR_INPUT: NoOf_DWORDs
    nMax_DB1 :DINT;     // max. No of DWORDs in blkDB1 beginning from DB1_ByteAdr up to END of DB
    nMax_DB2: DINT;     // max. No of DWORDs in blkDB2 beginning from DB1_ByteAdr up to END of DB
    nMax_DB_BOOL: DINT; // max. No of DWORDs in blkDB_BOOL beginning from DB1_ByteAdr up to END of DB

    fstLOOP : DINT;     // No of DWORDs to compare in the first LOOP (x*32)
    sndLOOP : DINT;     // No of DWORDs to compare in the second LOOP (it's the rest which do not fill a complete 32Bit result DWORD, max. 30)

    I:DINT;             // LOOP Counter
    K:DINT;             // LOOP Counter
    I32:DINT;           // I*32 for No of bits
    NoOfEqual : DINT;   // Variable for counting No of equal DWORDs

    DWD : DWORD;        // DWORD to store compare results 32 results packed in 1 DWORD
    ptrDWD AT DWD : ARRAY[0..31] OF BOOL;       // Because S7 SCL don't allow direkt Bit-Adressing by DWD.n, we have to use second view for DWD as ARRAY of Bits
    
    xVOID : BOOL;        // Not used return value for SFCs
    xRET : BOOL;         // Variable for FC-RET_VAL which stores the overall compare result (TRUE if all compared DWORDs are equal)

END_VAR
    
    // Call SFC_24 "TEST_DB" to get DB-Length
    wDBNo := BLOCK_DB_TO_WORD(blkDB1);
    RET   := TEST_DB(DB_NUMBER :=wDBNo, DB_LENGTH := wBYTEs, WRITE_PROT :=  xVOID);  // blk_DB1
    L_DB1 := WORD_TO_DINT(wBYTEs);  // total DB lLength in Bytes
    
    wDBNo := BLOCK_DB_TO_WORD(blkDB2);
    RET   := TEST_DB(DB_NUMBER :=wDBNo, DB_LENGTH := wBYTEs, WRITE_PROT :=  xVOID);  // blk_DB2
    L_DB2 := WORD_TO_DINT(wBYTEs); // total DB lLength in Bytes

    wDBNo := BLOCK_DB_TO_WORD(blkDB_BOOL);
    RET   := TEST_DB(DB_NUMBER :=wDBNo, DB_LENGTH := wBYTEs, WRITE_PROT :=  xVOID);  // blkDB_BOOL
    L_DB_BOOL := WORD_TO_DINT(wBYTEs); // total DB lLength in Bytes
     
    idxDB1 := INT_TO_DINT (DB1_ByteAdr);    // the Byte StartAdress of our Data in blkDB1
    idxDB2 := INT_TO_DINT (DB2_ByteAdr);    // the Byte StartAdress of our Data in blkDB2
    idxDB_BOOL := INT_TO_DINT (DB_BOOL_ByteAdr); // the Byte StartAdress of our compare results in blkDB_BOOL

    nDWORDs := INT_TO_DINT(NoOf_DWORDs);    // No of DWORDs from VAR_INPUT: NoOfDWORDs)
   
    // max. No of DWORDs
    
    nMax_DB1 := (L_DB1 - idxDB1)/4;     // max. poosible No of DWORDs from ByteStart in blkDB1 to DB_END
    nMax_DB2 := (L_DB2 - idxDB2)/4;     // max. poosible No of DWORDs from ByteStart in blkDB2 to DB_END
    nMax_DB_BOOL := (L_DB_BOOL -idxDB_BOOL) *8;   // max. NO of DWORDs in blkDB_BOOL can store *32/4 results    
    
    IF nDWORDs >0 THEN
        // nDWORDs is UserInOut
        nDWORDs := MIN(IN1:=nDWORDs, IN2:= nMax_DB1, IN3:= nMax_DB2);     // no of DWORDs to compare or max. possible to compare
    ELSE
        // if nDWORDs = 0 THEN use complete DB length for comparing
        nDWORDs := MIN(IN1:= nMax_DB1, IN2:= nMax_DB2);
    END_IF;
    
    fstLOOP := (nDWORDs /32)-1 ;  // no of DWORDs to compare in the 1st outer LOOP (I) == Zero based No of DWORDs which will fill a 32Bit result DWORD
    sndLOOP := (nDWORDs MOD 32) -1 ; // Rest No of DWORDs 0..30 which don't fill a 32Bit result DWORD, zero based
    
    NoOfEqual := nDWORDs;       // max. No of Equal DWORDs (counter for the No of equal)
    xRET := TRUE;               // set xRET := True, it will stay high as long as the compare results are true
        
    IF fstLoop >=0 THEN
        FOR I := 0 TO fstLOOP DO   // outer LOOP  DWORD Adress DD 
            
            I32:=I*32;
            
            FOR K :=0 TO 31 DO      // inner LOOP BitAdress .k
                IF blkDB1.DD[idxDB1+(I32+K)*4] = blkDB2.DD[idxDB2+(I32+K)*4] THEN
                    ptrDWD[K] := TRUE;  // the 2 DWORDs are equal, store Equal_Bit =TRUE
                ELSE
                    ptrDWD[K] := FALSE;  // the 2 DWORDs are not equal, store Equal_Bit = FALSE
                    xRET := FALSE;       // FC-RETURN = FALSE; not all DWORDs are equal
                    NoOfEqual := NoOfEqual-1;  // reduce No of equal DWORDs
                END_IF;
 
            END_FOR;

            IF I32 < nMax_DB_BOOL THEN  
                 blkDB_BOOL.DD[idxDB_BOOL+I*4]:=DWD;  // store the DWORD with the result Bits
            END_IF;
        END_FOR;
    END_IF;

    IF sndLoop >=0 THEN
        I := fstLOOP + 1; // Continoue with the last DWORD  
        I32 := I*32;

        IF I32 < nMax_DB_BOOL THEN
            DWD :=blkDB_BOOL.DD[idxDB_BOOL+I*4];    // Because less than 32 compare operations we must load org. value first
        END_IF;
        
        // because the rest is less than 32, we need only inner LOOP
        FOR K :=0 TO sndLOOP DO      // inner LOOP BitAdress .k
            IF blkDB1.DD[idxDB1+(I32+K)*4] = blkDB2.DD[idxDB2+(I32+K)*4] THEN
                ptrDWD[k] := TRUE;  // the 2 DWORDs are equal, store Equal_Bit =TRUE
            ELSE
                ptrDWD[k] := FALSE;  // the 2 DWORDs are not equal, store Equal_Bit = FALSE
                xRET := FALSE;       // FC-RETURN = FALSE; not all DWORDs are equal
                NoOfEqual := NoOfEqual-1;  // reduce No of equal DWORDs
            END_IF;
        END_FOR;
 
        IF I32 < nMax_DB_BOOL THEN
            blkDB_BOOL.DD[idxDB_BOOL+I*4]:=DWD;
        END_IF;
    END_IF;
    
    (* --------------------------------------------------
        FC-VAR_OUTPUT
       -------------------------------------------------- *)
  
    // RETRUN No of comppared DWORDS
    dNoOfComparedDwords := nDWORDs; 

    // RETURN No of DWORDs stored in the Equal-Bits in blkDB_BOOL     
    dNoOfStoredResultDwords := MIN(IN1:= nMax_DB_BOOL, IN2:=nDWORDS);

    // RETURN No of equal DWORDs
    dNoOfEqualDwords := NoOfEqual;      
    m7_BlockCompareDWORD := xRET;          // RETURN TRUE if all DWORDs compared are equal
END_FUNCTION
 
Hallo Maagic7

Deine Bausteine haben eine nicht erwähnte "Besonderheit": sie vergleichen auch den Inhalt von Padding-Bytes und Padding-Bits sowie unbenutzte STRING-Bytes, wodurch Unterschiede gemeldet werden könnten wo gar keine Daten-Unterschiede sind.

Leider kommen Deine Vergleichsbausteine einige Jahre zu spät. Die funktionieren nicht in aktuellen S7-1200/1500 CPUs, welche nach aktuellen Programmierempfehlungen programmiert sind - sprich die funktionieren nicht mit "optimierten" DB. Meine Meinung: man sollte Programmieranfängern, welche sich so einen einfachen Vergleichsbaustein nicht selber programmieren können, nicht zeigen, wie man es nicht (mehr) machen soll.

Harald
 
Deine Bausteine haben eine nicht erwähnte "Besonderheit": sie vergleichen auch den Inhalt von Padding-Bytes und Padding-Bits sowie unbenutzte STRING-Bytes, wodurch Unterschiede gemeldet werden könnten wo gar keine Daten-Unterschiede sind.

Mit Strings geht das nicht, Leerbytes sollten normalerweise kein Problem darstellen, da siese in einer Kopie den gleichen Wert (wohl 0) haben sollten.
AWL auf 1500er macht man besser nicht! Funktionieren müsste es eigentlich!

Gibt es ne bessere Lösung für einen allgemeinen Vergleich, als den DB in einer Schleife zu druchlaufen?

Mit den optimierten DB's wird das wohl Schwierigkeiten geben. Darüber hatte ich noch nicht nachgedacht.

Ich schalte die Optimierung der DBs grundsätzlich aus, da der Speicher meist groß genug ist und der Speichergewinn durch die Optimierung in den meisten Fällen nur sehr gering ist.

Die Funktion mit der Speicheroptimierung von DB's scheint mir eher ein Marketinginstrument von Siemens zu sein. Oder noch eher eine Methode um die HMI Konkurrenten auszusperren.
Wenn die Adressen nicht bekannt sind und die Methode wie die Optimierung von statten geht geheim bleibt! Ein Schelm der Böses dabei denkt!
 
Wenn die Adressen nicht bekannt sind und die Methode wie die Optimierung von statten geht geheim bleibt!
Ja, aber es gibt hier schon länger Lösungsansätze ( symbolischer Zugriff auf Variablen, also ohne bekannte Adresse ):
http://www.deltalogic.de/media/pdf/77/c4/0c/AGLink_5-3-elektrotechnik-Druckfreigabe.pdf

http://https://www.all-electronics.de/deltalogic-knackt-tia-symbolik/

Der optimierte Bausteinzugriff
erschwert es jedoch Anwendern, die
keinen Zugriff auf das TIA-Portal
haben, mit den S7-1200- und S7-
1500-Steuerungen zu kommunizie-
ren. Dies ist z.B. für Hersteller von
Panels und Visualisierungssoftware
entscheidend, die ihren Kunden die
Möglichkeit bieten möchten, sich
von außen an die Siemens-Welt
anzubinden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo
Ist es möglich bei einem Datenbaustein eine Datenänderung zu überwachen?
Habe einen DB mit 20 Byte Länge, und möchte so ca. alle 10s eine Abfrage starten, ob sich Daten in diesem Bereich geändert haben.
Danke
Hallo Klauserl,
Ich würde gerne fragen, hast du das Überwachungsproblem gelöst? Wenn ja, wie hast du das gelöst? Kannst du mir die Code oder Logik zeigen? Ich freue mich auf deine Antwort. Vielen Dank.
Hans
 
Zurück
Oben