TIA Lösung: 2 Structs miteinander vergleichen ohne vorher die Struktur zu kennen.

Balu_der_Bär

Level-2
Beiträge
111
Reaktionspunkte
44
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,
ich habe eine Lösung gefunden um 2 Structs miteinander zu vergleichen. Es gab dabei mehrere Probleme vorallem mit Strings die ihre vorherigen Einträge nicht löschen wenn dieser mit einem Kürzerer String überschrieben wurde

Ich benutze eine S7 1513-1 PN

Es wird ein "nicht bausteinoptimierter" DB benötigt der 2 Arrays mit jeweils einem Array[0..300] of Byte enthält ( die 300 kann natürlich variieren)

Code:
IN:
IN_1           : Variant
IN_2           : Variant

OUT :
Unterschied_erkannt : Bool

Temp:
i               : int
LEN_1        : dint
LEN_2        : dint
RET_VAL_1 : int
RET_VAL_1 : int
Unterschied : bool

//Structs in Array of byte kopieren
#RET_VAL_1 := Serialize(SRC_VARIABLE := #IN_A, DEST_ARRAY=>"GLB_HW_Vergleichs_DB".A , POS:= #LEN_1);
#RET_VAL_2 := Serialize(SRC_VARIABLE := #IN_B, DEST_ARRAY=>"GLB_HW_Vergleichs_DB".B, POS:= #LEN_2);


#Unterschied := false;

// arrays bytweise vergleichen und Array anschließend auf 0 setzen !Wichtig!
IF #LEN_1 = #LEN_2 THEN
    FOR #i := 0 TO #LEN_1 DO
        IF "GLB_HW_Vergleichs_DB".A[#i] <> "GLB_HW_Vergleichs_DB".B[#i] THEN
            #Unterschied := true;
        END_IF;
        "GLB_HW_Vergleichs_DB".A[#i] := 0;
        "GLB_HW_Vergleichs_DB".B[#i] := 0;
        If #Unterschied then
             EXIT;
        END_IF;
    END_FOR;
ELSE
// wenn Längen unterschiedlich dann sind die Structs eh unterschiedlich
    #Unterschied := true;
END_IF;

Unterschied_erkannt := Unterschied;

ich hoffe es nützt euch

Balu

*edit Anmerkungen von Harald eingebaut
*edit Anmerkungen von Peter eingebaut
 
Zuletzt bearbeitet:
Hallo Balu,

zunächst mal Danke für die Veröffentlichung Deiner Idee. Unter TIA funktioniert ja vieles nicht mehr so einfach.
Trotzdem ein paar Fragen und Kritiken.

1) Warum vergleichst Du die Arrays erst ab dem zweiten Byte?
2) Warum ist es wichtig, die Array-Bytes nach dem Vergleich auf 0 zu setzen?
3) Sobald der erste Unterschied gefunden wurde kann man den Vergleich (Schleife) abbrechen.
4) Es ist ungünstig, dem OUT "Unterschied" mehrmals Werte zuzuweisen. Besser eine interne Variable benutzen und am Ende die interne Variable auf den OUT kopieren.
5) Wo/Wie ist das Problem mit dem STRING-Vergleich gelöst?

Weil ich gerade kein TIA zur Verfügung habe:
6) Wie geht "Serialize" mit vom TIA versteckten Struct-Membern und mit Padding-Bytes und Padding-Bits um?
7) Wie ist die Repräsentation von Bools nach dem Serialize?

PS: Du könntest noch erwähnen, für welche SPS der Code gedacht ist.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Harald,
danke für deine Anmerkungen

1) Warum vergleichst Du die Arrays erst ab dem zweiten Byte?

Danke das war ein Fehler meinerseits.

2) Warum ist es wichtig, die Array-Bytes nach dem Vergleich auf 0 zu setzen?

Da muss ich ein wenig weiter ausholen auch in der Vermutung das ich viel bekanntes erzähle.

Wenn ein Variable of String in eine andere Variable kopiert wird, wird nur der "sichtbare" Teil kopiert + die Maximale und die genutzte länge.

Bsp:
Variable A und B sind vom Typ String[10]
A := 'test'
B := '1234567890'

Trotz der nur [10] Zeichen hat die Variable aber 12 Byte Länge, da vorweg noch die Maximale länge (10) und die tatsächlich genutzten Zeichen ( für A 4) stehen.

Kopiert man nun A nach B steht dort zwar Symbolisch B ='test' , schaut man sich jedoch den String als Array an sieht man das dort 'test567890' steht und nur durch den nicht sichtbaren "genutzte Länge" Teil der Rest nicht berücksichtigt wird.

Problematisch wird es wenn man einen Umweg über eine weitere Variable geht
A := 'test'
B := 'XXXXXX'
C := '1234567890'
D := ''
E := ''

Wenn wir nun folgende schritte abarbeiten:
D := C
D := B
D := A
E := C
E := A

Haben wir durch das mehrfache überschreiben zwar symbolisch in E und D 'test' stehen, aber wenn man sich das ganze Absolut anschaut steht in E 'test567890' und in D 'testXX7890'

Da man Strukturen nicht miteinander vergleichen kann muss man diese Byte weise vergleichen und dann haben wir das eben gezeigte Problem das trotz der Symbolischen Gleichheit es einen Unterschied gibt.

Man müsste also vor jedem Kopiervorgang die variable D und E initialisieren.

Wenn man auf die absolute Adressierung verzichten will, fallen die normalen Move befehle
( ":=" , "Move_blk_variant") weg, da diese nur 2 gleichen Datentypen zum kopieren akzeptieren und ich nicht weiß wo sich der String befindet den ich löschen will.

Daher nutze ich den "Serialize" Befehl, der die ganze Struktur in ein Array of Byte kopiert und überschreibe nach jedem vergleichen das Byte mit 0, damit beim nächsten kopieren die "Phantomzeichen" gelöscht sind.


3) Sobald der erste Unterschied gefunden wurde kann man den Vergleich (Schleife) abbrechen.

in meinem Fall überprüfe ich zyklisch ob sich die Eingabe im Editorfenster gegenüber dem gespeichertem im Array geändert hat. Dies wird in 99,9% der Fälle nicht der Fall sein , daher habe ich darauf verzichtet da es auf das eine Mal dann auch nicht mehr drauf an kommt. In anderen Bereichen kann das aber durchaus Sinn machen ja.


4) Es ist ungünstig, dem OUT "Unterschied" mehrmals Werte zuzuweisen. Besser eine interne Variable benutzen und am Ende die interne Variable auf den OUT kopieren.
Hast du recht, ändere ich ab.

5)Wo/Wie ist das Problem mit dem STRING-Vergleich gelöst?

siehe 2

6) Wie geht "Serialize" mit vom TIA versteckten Struct-Membern und mit Padding-Bytes und Padding-Bits um?

Ich denke das alle Informationen dort abgebildet werden.In meinem Beispiel hatte ein String der Länge[10] 22 Byte belegt.Da ich davon nur 12 Byte zuordnen kann muss da noch einiges verstecktes drin sein.
Serialize ist dafür gemacht um ein Struct über einen Seriellen Bus auf eine andere SPS zu übertragen. Das Gegenstück nennt sich dann Deserialize. Um das Struct exakt kopieren zu können müssen ja alle Informationen vorhanden sein.
Aber mehr kann ich dir dazu nicht sagen.

7) Wie ist die Repräsentation von Bools nach dem Serialize?
müsste ich ausprobieren, komme ich aber heute nicht mehr zu
 
Hallo Balu,
ich habe mich heute mit deinem Script beschäftigt.
Erstmal Besten Dank dafür!

Es sind mir ein paar Unstimmigkeiten aufgefallen:
3) Sobald der erste Unterschied gefunden wurde kann man den Vergleich (Schleife) abbrechen.
Der Abbruch innerhalb der Fallentscheidung auf ungleich verhindert eine Initialisierung des ByteArray mit 0.
Nachdem ein Unterschied erkannt wurde hat dann die Auswertung nicht mehr richtig funktioniert.
Beim Nachladen des Parametersatz blieben so scheinbar einige Byte beschrieben, ein Unterschied war geboren...


Ich habe den Schleifenabbruch versetzt und so funkt. alles bisher:

Code:
IF #LEN_1 = #LEN_2 THEN
    FOR #i := 0 TO #LEN_1 BY +1 DO
        IF "VergleichDB".A[#i] <> "VergleichDB".B[#i] THEN
            "VergleichDB".Unterschied := true;
        END_IF;
        "VergleichDB".A[#i] := 0;
        "VergleichDB".B[#i] := 0;
        IF "VergleichDB".Unterschied THEN   EXIT;   END_IF;
    END_FOR;
ELSE


LG
Peter
 
Hallo Peter,
danke für die Information habs oben schon abgeändert.

inzwischen wird man dieses Code jedoch kaum noch brauchen, da es mit V14 möglich ist den inhalt von 2 Datentypen direkt miteinander zu vergleichen

Code:
If datentyp1 =datentyp2 then
;
end_if;

leider ist dort noch ein Bug drin der es nicht zulässt in SCL datentypen eines arrays zu vergleichen. Hab Siemens das schon mitgeteilt und sie geloben Besserung :D

bis dahin muss man für den vergleich zuerst die Datentypen in einen Temporären bereich kopieren und dann vergleichen.

grüße

Balu
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Es sind mir ein paar Unstimmigkeiten aufgefallen:
3) Sobald der erste Unterschied gefunden wurde kann man den Vergleich (Schleife) abbrechen.
Der Abbruch innerhalb der Fallentscheidung auf ungleich verhindert eine Initialisierung des ByteArray mit 0.
Nachdem ein Unterschied erkannt wurde hat dann die Auswertung nicht mehr richtig funktioniert.

Hähhh? :confused: Was hat das Ablöschen des unterschiedlichen Bytes in beiden Arrays nach dem Vergleich mit dem Vergleichsergebnis zu tun? Warum mußt Du die beiden Bytes ablöschen, was hat bei Dir nicht richtig funktioniert?

PS: Übrigens bezeichnet das Wort Initial etwas, was man vorher macht und nicht nachher. "Initialisierung" ist der falsche Ausdruck für das was Du da meinst - kann es sein, daß Du vielleicht auch eine falsche Vorstellung hast von dem was der Code tut? Der Code von Balu funktioniert sowieso nur eingeschränkt und keinesfalls universell, weil er auch von den Daten nicht belegten Speicher (Padding) mit vergleicht - er kann nicht prüfen, ob die Strukturen überhaupt gleich aufgebaut sind oder ob die Datentypen gleich sind. Ein simples Kopieren des Codes für eigene abweichende Zwecke kann zu Fehl-Meldungen über Unterschiede führen.


Hallo Peter,
danke für die Information habs oben schon abgeändert.
Hallo Balu,
besser Du verbesserst nicht sofort angebliche Bugs in Deinem Beitrag #1, irgendwann sieht man nicht mehr das Konzept.

inzwischen wird man dieses Code jedoch kaum noch brauchen, da es mit V14 möglich ist den inhalt von 2 Datentypen direkt miteinander zu vergleichen

Code:
If datentyp1 =datentyp2 then
;
end_if;
Hast Du eine Ahnung, was für eine Zykluszeit-Bombe Du Dir mit der einen unscheinbaren Zeile in Dein Programm holst, wenn das Programm den Vergleich nicht immer macht? :cool:

Harald
 
Hast Du eine Ahnung, was für eine Zykluszeit-Bombe Du Dir mit der einen unscheinbaren Zeile in Dein Programm holst, wenn das Programm den Vergleich nicht immer macht? :cool:

Harald

Effektiver Code interessiert doch heute keinen mehr, schau mal ins Forum da wimmelt es von Schleifen und Strings ;-)
 
Umgebung:
TIA V13 SP1 Upd9 mit PLCsim und nettoPLCsim mit WInCC Advanced Simulation am PC

Hier eine Beschreibung:

Befindet sich der EXIT innerhalb der IF Abfrage und ein Unterschied der serialisierten UDT's wird erkannt, wird dem Bit "VergleichDB".Unterschied "true" zugewiesen.
Soweit, so gut.
Werden nun neue, gleiche Daten in die UDT's geladen und kommen im nächsten Zyklus serialisiert (je 49 Byte) in den Schleifenvergleich, dann wird weiterhin ein Unterschied erkannt.

Befindet sich der EXIT nach der Initialisierung innerhalb der Schleife, funktioniert alles!
Initialisierung deshalb, da die Schleife alle Elemente auf einen definierten Zustand für den kommenden Schleifendurchlauf bringt.
Also wird es ja gewissermaßen vorher gemacht...

Letztlich sah ich die Ursache für das Verhalten in der enthaltenen String Variable:
2) Warum ist es wichtig, die Array-Bytes nach dem Vergleich auf 0 zu setzen?


Ich bedanke mich für eure Zeit und bin offen für andere Herangehensweisen.

Peter
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Befindet sich der EXIT nach der Initialisierung innerhalb der Schleife, funktioniert alles!
Initialisierung deshalb, da die Schleife alle Elemente auf einen definierten Zustand für den kommenden Schleifendurchlauf bringt.
Also wird es ja gewissermaßen vorher gemacht...
Nein, nicht vorher, denn Du "initialisierst" für einen nächsten Schleifendurchlauf - und wer "initialisiert" die Elemente VOR dem ersten Durchlauf? Nach Deiner Erklärung vergleichst Du beim ersten Durchlauf sozusagen nicht initialisierte Elemente und initialisierst erst nach dem Vergleich - Was soll der Sinn hinter einem solchen Vorgehen sein?

Außerdem: selbst wenn Du nach dem Vergleich erst noch die beiden unterschiedlichen Bytes "initialisierst" und dann die Schleife verläßt - wie werden die Elemente hinter dem ersten Unterschied "initialisiert"? Da können ja auch noch Unterschiede sein, die beim nächsten mal noch nicht "initialisiert" wurden... Dein Vorgehen funktioniert nur scheinbar oder zufällig. Die Ursache für Dein Problem muß woanders liegen. Theoretisch sollte doch das Serialize() sämtliche Bytes des Ausgabe-Arrays definiert beschreiben - "initialisieren"! Hast Du das mal alleine getestet?
Also wenn die Serialize-Funktion wirklich so scheixxe funktioniert, daß sie nicht initialisierte String-Restbytes oder Padding-Bytes in das Ausgabe-Array einstreut, dann taugt das ganze Vorgehen überhaupt nichts und das Ausgabe-Array muß VOR dem Serialize() komplett gelöscht/initialisiert werden.


Werden nun neue, gleiche Daten in die UDT's geladen und kommen im nächsten Zyklus serialisiert (je 49 Byte) in den Schleifenvergleich, dann wird weiterhin ein Unterschied erkannt.
Klingt ja unglaublich! Höchstens Deine UDT enthalten Strings, die nicht max-voll sind - doch auch dann dürfte Dein Beschreiben des Ausgabe-Arrays im Durchlauf vor dem erneuten Serialize() keinen Einfluß haben. Es sei denn, das Serialize() funktioniert nicht richtig.

Kannst Du uns mal zeigen wie Deine 49 Byte langen UDT deklariert sind? Und wie das aus Serialize() resultierende Byte-Array aussieht (welche Werte in den einzelnen Bytes stehen)?


PS: irgendwie scheint dieses Serialize() und Deserialize() so ziemlich die krankhafteste Lösung zu sein, aus "optimiertem" Speicher einen Speicherbereich mit kontrollierter Struktur zu machen. Wenn ich feststellen würde, daß ich Serialize() und Co brauche, dann wäre das ganz klar das Argument, daß der betreffende Speicherbereich "nicht-optimiert" gehört.

Harald
 
Tests

So, ein paar Tests:
Exit innerhalb der IF Abfrage

1. Flanke: gespeicherte Datensätze[1] --> Projektierfelder.Datensatz : danach im Projektier Feld die Bezeichnung auf 'Test 11' erweitert. Unterschied erkannt!
2. Flanke: gespeicherte Datensätze[2] --> Projektierfelder.Datensatz : es fehlen schon manche String Byte im Array des VergleichDB... INT und TIME sind korrekt.
3. im FB die Zeilen mit der 0-Zuweisung und EXIT aus kommentiert --> die Byte im Array sind nun richtig aus "Projektierfelder.Datensatz" serialisiert worden!

VergleichDB.A entspricht dem serialisierten Projektierfelder.Datensatz.
Das Problem tritt nur auf, wenn der nachfolgende String kürzer ist.
Was mit 'Test 2' gegenüber 'Test 11' der Fall war.

War ja im ersten Beitrag auch schon so beschrieben:
Es gab dabei mehrere Probleme vorallem mit Strings die ihre vorherigen Einträge nicht löschen wenn dieser mit einem Kürzerer String überschrieben wurde


Ist das EXIT außerhalb der IF Abfrage innerhalb der Schleife funktioniert alles wie erwartet.

LG
Peter
 

Anhänge

  • 1..JPG
    1..JPG
    476,3 KB · Aufrufe: 41
  • 2..JPG
    2..JPG
    480,8 KB · Aufrufe: 30
  • 3..JPG
    3..JPG
    470,1 KB · Aufrufe: 27
Hallo Balu & Peter,

Mann o Mann... :roll: offensichtlich seid Ihr Beide nicht in der Lage, den von Euch verwendeten Code richtig zu testen und die Ursache für das fehlerhafte Verhalten zu finden. Ich habe das nun für Euch gemacht (TIA V13 SP1 Upd8 + PLCSIM für CPU1515-2PN) :cool:
Mann o Mann... wenn man Steuerungssoftware für produzierende Anlagen schreibt, dann sollte man 100% durchsehen, was man da tut und nicht schon zufrieden sein, wenn der Compiler keine Fehler mehr auswirft und es mit 3 Testdatensätzen anscheinend funktioniert.

Worauf hatte ich schon in Beitrag #2 hingewiesen?
2) Warum ist es wichtig, die Array-Bytes nach dem Vergleich auf 0 zu setzen?
[...]
5) Wo/Wie ist das Problem mit dem STRING-Vergleich gelöst?
[...]
6) Wie geht "Serialize" mit vom TIA versteckten Struct-Membern und mit Padding-Bytes und Padding-Bits um?
Offensichtlich habt Ihr Beide das nicht analysiert...


So funktioniert der Vergleich ohne das sinnfreie "Initialisieren NACH dem Vergleich" (davon abgesehen, daß der Code nicht weiß, ob er vielleicht Äpfel mit Birnen vergleicht, wenn sie nur gleich groß sind):
Code:
[COLOR="#008000"]//Vergleichs-Arrays vor dem Serialize() initialisieren[/COLOR]
[COLOR="#008000"]//(effizienter ginge ein Kopieren eines Arrays, wo schon alle Bytes 0 sind)[/COLOR]
FOR #i := 0 TO 99 DO
    "VergleichDB".A[#i] := 255; [COLOR="#008000"]//für Analyse des Serialize() ist Initialisierung mit 16#FF besser geeignet[/COLOR]
    "VergleichDB".B[#i] := 255;
END_FOR;

[COLOR="#008000"]//Structs in Array of Byte kopieren[/COLOR]
#LEN_1 := 0; #LEN_2 := 0;  [COLOR="#008000"]//warum ist Serialize.POS ein InOut???[/COLOR]
#RET_VAL_1 := Serialize(SRC_VARIABLE := #IN_1, DEST_ARRAY => "VergleichDB".A, POS := #LEN_1);
#RET_VAL_2 := Serialize(SRC_VARIABLE := #IN_2, DEST_ARRAY => "VergleichDB".B, POS := #LEN_2);

#unterschied := TRUE;
[COLOR="#008000"]//Arrays byteweise vergleichen[/COLOR]
IF #LEN_1 = #LEN_2 THEN
    #unterschied := FALSE;
    #LEN := DINT_TO_INT(#LEN_1);
    FOR #i := 0 TO #LEN DO
        IF "VergleichDB".A[#i] <> "VergleichDB".B[#i] THEN
            #unterschied := TRUE; [COLOR="#008000"]//Unterschied gefunden! Weiterer Vergleich unnötig[/COLOR]
            EXIT;                 [COLOR="#008000"]//Vergleich abbrechen[/COLOR]
        END_IF;
    END_FOR;
END_IF;

[COLOR="#008000"]//Vergleichsergebnis auf Output geben[/COLOR]
#Ungleich := #unterschied;

Ursache für das fehlerhafte Verhalten Eurer Programmversionen ist tatsächlich so wie ich bereits in #9 vermutet habe, daß Serialize() nicht von Daten belegten Speicher im Ausgabe-Array einfach unbeschrieben/uninitialisiert läßt! Deshalb ist es nötig, das Ausgabe-Array VOR Ausführung von Serialize() selber komplett zu initialisieren.

Wenn Siemens das deutlich so dokumentieren würde, dann könnte man damit leben und Fehlverhalten vorbeugen. Ich halte solches Verhalten allerdings für einen Bug.

Die Dokumentation zu Serialize() ist aber schwammig, unvollständig und auch fehlerhaft (z.B. DEST_ARRAY ist kein InOut, sondern ein Out). Warum ist DEST_ARRAY ein Out und trotzdem werden nicht alle Elemente in dem Ausgabe-Array beschrieben? Warum eigentlich ist der Parameter POS ein InOut?


Es scheint so, als ob Siemens ab Step7 V14 an der Arbeitsweise des Serialize unauffällig Änderungen vorgenommen hat, jedenfalls taucht nun in der Beschreibung zum Serialize() dieser Satz auf:
Systemhandbuch Step7 Prof V14 zu Serialize schrieb:
Es kann dazu kommen, dass Elemente des ARRAY of BYTE mit Nullen aufgefüllt werden, um den Standard-Ausrichtungsregeln zu genügen.

In Step7 V13 bleiben die Padding-Bytes und -bits einfach uninitialisiert, ab V14 sollen ("können") zumindest die Padding-Bytes auf 0 initialisiert sein.
Weiter unklar bleibt: werden auch Padding-Bits initialisiert?
Und wie behandelt Serialize() unbelegte String-Bytes?
Und wie verhält sich Serialize() wenn Strings fehlerhafte Header haben?

Vielleicht kann das mal jemand mit V14 testen.


Daß bei der Beschreibung des "Serialize" auf "TSEND" verwiesen wird, läßt mich befürchten, daß beim senden von "optimierten" Daten per TSEND ähnliche Mülldaten mit unkontrolliertem/zufälligem Inhalt in die Sendedaten eingefügt werden ...

Harald
 
Zurück
Oben