Step 7 Auswertung Array of Char

Matze001

Level-3
Beiträge
2.814
Reaktionspunkte
573
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Leute,

ich stehe vor folgender Aufgabe:

Ein Teilnehmer wird mittels TCP/IP Kommunikation abgefragt.
Ich habe alles am Laufen, es funktioniert alles zufriedenstellend, ABER:

mein Bautein hat in SCL 1200 Zeilen, und auf einer IM151-8F PN/DP eine Zykluszeit von 21ms.

Was wird dort gemacht, bzw. was ist die Bremse?

Ganz einfach:

Ich werte ein Array of Char aus, in dem 4000 Chars kommen.
Die ersten 280 Char werden nicht angeguckt, da sie unwichtige Header-Infos enthalten.
Die Daten die danach kommen, sehen etwa so aus:

Code:
Variable1=123456.123456,Variable2=4321.1,Variable3=123

Wenn nun Variable 1 nicht 123456 ist, sonder nur 1.0 dann verschieben sich die Positionen von Variable 2-n natürlich in meinem Array.

Wie ist mein vorgehen bisher:

Ich weis wo meine Daten ungefähr ankommen.
Dann füge ich eine FOR-Schleife ein, die an dieser Position +-20 Zeichen eine Bestimmte zeichenkette sucht, das ende der Variable sucht, die Variable in einen String kopiert und dann in ein passendes Datenformat konvertiert... etwa so:


(Wichtige Info: Code ist grad aus dem Kopf runtergetippt, muss nicht 100% korrekt sein, es geht ums Prinzip)
Code:
// Suchen nach Variable 1
FOR I:=280 TO 320 DO

// Finden der Zeichenfolge
IF CHARARRAY(i) = 'V' AND
CHARARRAY(i+1) = 'A' AND
CHARARRAY(i+2) = 'R' AND
CHARARRAY(i+3) = '1' AND
CHARARRAY(i+4) = '=' 

THEN

// Ich weis wo meine Variable anfängt (nach dem "=")
TMP_FOUND:=i+5;

END_IF;

// Wenn der Anfang der Variablen gefunden wurde...
IF_TMP_FOUND > 0 THEN

// Die Variable bis zum Punkt auslesen und in einen String kopieren(vereinfacht, eigentlich suche ich an dieser Stelle von i ab 
//nach dem Punkt oder Komma, je nachdem ob ich Nachkommastellen brauche oder nicht)

TMP_EXIT:=FALSE;
TMP_STRING:=' ';
FOR J:=i TO i+6 DO

TMP_STRING:=CONCAT(IN1:=TMP_STRING, IN2:=CHARARRAY(J);

END_FOR;

// Nach dem erstellen des String den String in DINT wandeln

MY_DINT:= STRING_TO_DINT(TMP_STRING);

TMP_EXIT:=TRUE;
EXIT; // Spart nen Haufen Zykluszeit
END_IF;

IF TMP_EXIT:=TRUE THEN
EXIT; // Nach verlassen der ersten FOR-Schleife auch die Verschachlte verlassen)
END_IF;

END_FOR;


Dieser ganze Code ist

1. Total aufgebläht und meiner Meinung nach recht "unschön" und schwer verständlich
2. Frisst er abartig Zykluszeit, weil ich durch diverste Schleifen renne

Diesen Code führe ich für etwa 20 Variablen durch, entsprechend erhöht sich die Zykluszeit.

Jetzt wäre es für mich interessant zu wissen wie ihr sowas löst! Ich denke, dass ich nicht der Einzige sein kann der eine recht große Menge von CHARS sinnvoll auslesen muss. Ich denke bei einem IPC würde mich das nicht interessieren, da fällt mir die Zykluszeit vermutlich nicht mal auf, bei der kleinen Muckelkiste von IM-CPU ist es aber relevant (und bevor jemand es vorschlägt: NEIN, ich kann keine andere CPU verwenden!)

Ich freue mich auf eure Ideen und Vorschläge! Ich habe von puzzlefreak schon einmal ein paar Ansätze bekommen, und habe die Zykluszeit von 58ms auf 21ms senken können, ich hoffe nun das noch mehr geht. (ich brauche hier keine 2ms Zykluszeit, aber je schneller der Code wird, desto mehr Abfragen könnte ich noch machen)

Grüße

Marcel
 
Auf die Schnelle... probiere mal diese Änderung:
Code:
// Suchen nach Variable 1
FOR I:=280 TO 320 DO
// Finden der Zeichenfolge
IF CHARARRAY(i) = 'V' THEN
* IF CHARARRAY(i+1) = 'A' THEN
** *IF*CHARARRAY(i+2) = 'R' THEN
** * *IF*CHARARRAY(i+3) = '1' THEN
** * * *IF CHARARRAY(i+4)*= '='*THEN
** * * * *// Ich weis wo meine Variable anfängt (nach dem "=")
** * * * *TMP_FOUND:=i+5;
** * * *END_IF;
** * *END_IF;
** *END_IF;
**END_IF;
END_IF;
Weiters: Warum beginnt Deine j-Schleife mit i und nicht mit TMP_FOUND? Kommt das vom Tippen aus dem Gedächtnis?

Harald's Galaxy Tab

PS: Die Programmierer von Android müsste man alle erschlagen oder zwingen, mal selber einen einfachen Text wie diesen zu erstellen..... :evil:
Und die eigenmächtigen Textmanipulationen des Beitragseditors sind auch idiotisch. Ignoriere die * die sind nicht von mir. Ich gebs auf.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Wichtig: probiere Deinen Code auch mit Text aus, der garantiert NICHT enthält was Du suchst, damit alle Schleifen maximal durchlaufen werden, und schau dann mal auf die Zykluszeit.

Harald
 
Wichtig zu wissen wären erstmal ein paar Eckdaten wie der auszuwertende Code denn nun wirklich aussieht:

1. Ist der aufbau immer konsistent? D.h. kommt nach deinen 280 Bytes immer ein VAR1= ? Oder was könnte da sonst stehen? Vielleicht kannst du uns ja mal ein typisches schnipsel geben...
2. Max. und Min. Anzahl an Vor- und Nachkommastellen bitte nennen.

Ich denke, der Ansatz vom pn/dp ist schon eine ecke schneller, da er schon aus der ersten Schleife springt, wenn das v nicht findet. Seiner wertet quasi ja noch 4 stellen mehr aus... 4 zeilen mehr Code= länge Zykluszeit...

Da ich deinen weiteren Code jetzt nicht kenne, würde ich pn/dps Ansatz erstmal nehmen, nach einer gefundenen Variable aber mir den Offset zur nächsten VAR merken, und damit dann den auszuwertenden Code kleiner machen...

Andere frage: Muss das Zyklusgenau sein? Hast die Daten doch eh im Puffer stehen... wenn du jetzt davon ausgehst, dass du eine max Anzahl von Variablen x hast, könntest du bei jedem Zyklus nach einem var[x] in deinem Puffer suchen, und nach dem durchlauf inkrementieren... dies nur als Beispiel zur asynchronen abarbeitung...
 
Zuletzt bearbeitet:
Hallo Ihr zwei,

danke für eure Antworten:

PN/DP
1. ja die zweite For-Schleife beginnt eigentlich mit TMP_FOUND und endet mit TMP_FOUND + n.
2. Die verschiedenen IF-Abfragen sind gut -> Danke wird umgesetzt

Mnuesser


1. Eckdaten: Die erste Variable kommt immer an der Gleichen stelle, und dann ist es halt abhängig davon wie die Variablen ankommen, wie weit die dahinter verschoben sind. d.h. ist VAR1 sehr groß, ist Var2 ca. 2-5 CHARS weiter hinten, ist VAR2 auch sehr groß, ich Var3 weiter hinten etc.
2. es sind max. 6 stellen vor, und 6 Stellen nach dem Komma möglich (es gibt auch Sonderdinger, da steht was anderes drin, die beachte ich aber erstmal nicht)
3. nen Groben Aufbau habt ihr ja, es kommen hintereinander die verschiedenen variablen, mehr nicht.

4. Muss es Zyklusgenau sein: Jein. Meine Funktion ist z.Zt. so:

Wenn neue Daten ankommen (T_RECV.NDR) wird der Puffer ausgewertet und alles weggeschrieben.
So habe ich sichergestellt, das ich nichts verpasse.

Ich müsste mal nen Counter einbauen, wie viele Zyklen keine neuen Daten kommen. Wenn das ausreichend viele sind kann ich mir auch vorstellen das auswerten auf mehrere SPS-Zyklen zu legen. Das hatte ich schon im Hinterkopf, hatte es aber zunächst nicht umgesetzt, weil ich mir noch nicht sicher bin ob ich mir dadurch vielleicht Probleme ins Haus hole. Wenn z.B. nur alle 5-10 SPS-Zyklen neue Daten kommen, dann kann ich ruhig die Auswertung auf 2-3 Zyklen legen. Mein Ansatz hierfür wäre aber ein anderer:


Vorweg zum Beispiel: Mir ist klar das in dieser "Schrittkette" auch alle Schritte in einem Zyklus abgearbeitet werden, müsste sie halt verdreht aufbauen, oder noch ein Bit zum Sperren des nächsten Schritts einbauen.
Code:
IF NDR THEN

// Auslesen von 5 Variablen
SK_NR:=10;

END_IF;

IF SK_NR = 10 THEN

// Auslesen der nächsten 5 Variablen

SK_NR:=20;

END_IF;

ETC!



Ich danke euch beiden schonmal für die Ansätze, ich werde mal berichten wie sich die Änderungen ausgewirkt haben!

Grüße

Marcel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn das Programm auch auch ungültige Datensätze erkennen und behandeln können soll, ist womöglich ein anderer Ansatz erforderlich.

Was soll das Programm beispielsweise machen, wenn ein Satz wie:
Variable1=1Variable2=4321.1

oder
Variable1=1x2,Variable2=4321.1

hereinkommt?

Dein jetziges Programm scheint solche Fälle zu ignorieren oder sich womöglich daran zu verhaspeln und ungültige Werte auszugeben.

Ein etwas theoretischerer Ansatz:
Um die Sätze auf Korrektheit überprüfen zu können, benötigt man aber ersteinmal eine Syntax-Definition wie ein gültiger Satz aufgebaut sein muss. Für die Beschreibung solcher Dinge gibt es eigene Sprachen, z.B. EBNF (http://de.wikipedia.org/wiki/Erweiterte_Backus-Naur-Form).

Anhand deiner Beispielsätze würde diese womöglich so aussehen:

Code:
assignmentList = assignment { "," assignment }.
assignment = identifier "=" ( intNumber | realNumber ).
identifier = letter { letter } intNumber.
intNumber = digit { digit }.
realNumber = digit { digit } "." digit { digit }.
digit = "0123456789".
letter = 'A' .. 'Z' + 'a' .. 'z'.

Dein Zeichenarray das du zugesenden bekommst ist sozusagen eine Aussage in der Sprache "assignmentList".
Kleine Erläuterung zu den Zeichen: Geschweifte Klammern {} bedeuten, dass das darin enthaltene Symbol optional wiederholt werden kann.

Anhand der Syntax lässt sich nun ein Parser programmieren.
Ob dieser Parser der sich dann ergibt schneller als dein jetziger wird lass ich mal dahingestellt, auf jeden Fall wird er bei weitem keine 1200 Zeilen benötigen und zuverlässig Fehler erkennen können.

PS
Ein Beispiel wie man so einen Parser programmieren könnte habe ich hier mal gepostet:
http://www.sps-forum.de/simatic/50282-einfacher-formelparser-scl.html
 
Hallo Thomas,

danke für deine Antwort!

Es kann nicht vorkommen, dass solche Fehler passieren wie von dir beschrieben, im Zweifel wird nichts empfangen.
Mein Partner der mir die Daten sendet ist in der Hinsicht recht gut ausprogrammiert. Wenn es auch in anderer Hinsicht mal so wäre...
Deshalb bin ich darauf ich nicht näher eingegangen was passiert wenn dieser Fall eintritt, wenn aber z.B. Variable1=Variable2=1 kommen SOLLTE, dann steht in Variable1 nach der Wandlung nach DINT eine 0, dann STRING_TO_DINT() keine Zahl erkennt und 0 ausgibt! Diese Reaktion ist okay für mich.

Noch zur Info zum 1200 Zeilen "Trümmer" in SCL:

Die 1200 Zeilen machen nicht nur die Auswerten, dahinter verbirgt sich noch der Aufruf von T_SEND, T_RECV, das aufbereiten von Sendedaten, und ca. 50% der Zeilen machen meine Kommentare aus.

Grüße

Marcel
 
Die Variablen kommen doch immer in der gleichen Reihenfolge / es fehlt keine dazwischen oder?

Wo startet dann deine Suchen nach Var2, wieder bei 280?

Da ließe sich sparen mit:

FOR I:=280 + Länge VarBisher TO Ende - Länge VarAktuell DO
(Wenn nicht schon gemacht)

Oder die Vergleiche komplett weglassen:
Einmal über das Array laufen und die Positionen von = . und , abspeichern.(Array of Struct - 3xINT)
Dann ist klar wo die Werte für Variable 1-x stehen.
Geht natürlich massiv daneben, wenn eine Variable fehlt.

Generell:
TMP_STRING:=' ';
Unsicher was der Compiler damit macht, evtl. ist es schneller eine 0 an das zweite String-Byte zu schreiben.

Concat in der Schleife sieht auch nicht besonders performant aus.
Würde ich vergleichshalber mal einen SFC 20 BLKMOV probieren.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Pipboy,

danke für dein Antwort!

Reihenfolge ist immer gegeben! Ich fange NICHT immer wieder bei 280 an, sondern ich weis ja wo in etwa die Variablen ankommen (Variable1 bei etwa 280, Variable2 bei etwa 320, Variable3 bei etwa 360, etc...) Diese Positionen +-10 nutze ich für die FOR-Schleifen zum Suchen nach den einzelnen Variablen. Das ist effektiver als eine komplette For-Schleife über das ganze Array zu jagen, und dann wieder mit Schleifen zu suchen.

TMP_STRING:= ' '; läuft bisher immer sauber! Im zweiten Byte steht die Längeninfo vom String wenn ich mich nicht irre? Wäre auch ne Idee (aber wieder weniger lesbar / nicht symbolisch)

CONCAT ist nicht performant, ist mir klar. Bisher ist mir nur noch keine sinvollere Operation eingefallen um Strings aneinander zu bauen (Wie ich das mit dem SFC20 machen soll fällt mir adhoc auch nicht ein!)

Ich versuche so gut es geht auf die Funktionen von Siemens zu verzichten (FIND und DELETE konnte ich z.B. schon ersetzen)

Grüße

Marcel
 
Hallo,

ich würde ebenfalls nur einmal von 280 bis zum Ende das Byte-Array durchgehen und nicht erneut die nächste Var. suchen. Höchstwahrscheinlich würde ich sogar direkt die Ziffern in DINT/REAL umwandeln. Mit dem gleichen Aufwand, wie Du den String für STRING_TO_DINT() zusammenstellst, kann man auch gleich rechnen:
Zwischenwert = Zwischenwert * 10 + ASCIIziffer - '0'
(ein Algorithmus für die Nachkommastellen würde mir dann auch wieder einfallen ;))

Da - wie Du sagst - kein Formatfehler in dem Zeichenstrom enthalten sein kann ;) würde ich beim ersten unerwarteten Zeichen einfach die Konvertierung komplett abbrechen und verwerfen, statt Whitspaces und sonstiges auszuwerten.

Harald
 
Hallo Harald,

Wenn ich das ganze Array durchgehe, braucht das wieder ewig Zykluszeit (Das ganze Telegramm ist eher für PCs optimiert, deshalb bekomme ich zu den Variablen noch ein paar Infos die ich aber in der SPS nicht benötige). Es gibt auch kein Zeichen von dem ich auf die Variable schließen kann die ich suche!

Das mit dem Rechnen ist natürlich ein Ansatz! Stringoperationen zu vermeiden wird sicher auch Zykluszeit sparen!

Grüße

Marcel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn ich das ganze Array durchgehe,
wolltest Du nicht? mußt Du nicht sowieso das ganze Array durchgehen??? (*)
Willst Du nur ein paar Werte aus dem Text entnehmen? Merkst Du Dir irgendwie, welche Werte Du schon gefunden hast?

(*) Dann hätte ich Deine Aufgabenstellung nicht richtig verstanden und müsste nochmal nachlesen...

Harald
 
Okay ich beschreibe es nochmal:

Das Telegramm sieht etwa so aus:

Die Zahlen sind die Zeichen..

0-280 Header
280-290 Variable 1
310 - 340 Variable 2 (Varianz wegen länge variable 1)
350-380 Variable 3 (Varianz wegen länge var1 + var2)
...

Ich muss also um 3 Variablen zu lesen nicht 380 zeichen durchsuchen, sondern einmal 10, und zweimal 30. (Also 70 Zeichen) Zum Ende des Strings werden es natürlich mehr Zeichen, da die Verschiebung zunehmen kann. Somit habe ich statt 380 Schleifendurchläufen nur 70 -> sehr viel gespart.

Grüße

Marcel
 
Ah ja, es sind zusätzliche nicht relevante Informationen enthalten. Sind "Deine" Werte wenigstens bunt gemischt an wechselnden Positionen enthalten? Dann mußt Du im worst case sowieso den ganzen Text durchgehen - und der worst case bestimmt Deine maximal mögliche Zykluszeit, mit der Du rechnen mußt.

Ebenso Dein "an dieser Position +-10 beginnt der Wert" steht auf sehr tönernen Füßen. Laß mal viele Variablen mit "glatten" Werten mit wenig Nachkommastellen kommen, dann sind Deine Werte sehr schnell aus dem Suchfenster gewandert.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die Reihenfolge ist immer gleich. Deshalb benötige ich nur das Fenster.
Das Fenster wird zum Ende hin immer breiter, es fängt mit +-10 an, und endet z.Zt. bei +-40 zeichen. Wenn mehr dazu kommt, muss das natürlich hochgeschraubt werden.

Was ich auch ein wenig unterschlagen habe:

Die ersten 10 Variablen haben eine Konstante länge, danach fängt das wandern an, und es handelt sich z.Zt. nur um 5 Variablen.
Deshalb ist alles in einem kleinen Rahmen.

Grüße

Marcel

P.S: Bevor jemand meckert das ich Infos weglasse: Das mit den ersten 10 Konstanten Variablen habe ich bewusst weggelassen, um es nicht noch verwirrender zu machen.
 
Wieviele Werte willst Du eigentlich entnehmen? Stehen die alle nacheinander ohne Fremddaten dazwischen und danach kommt nur noch uninteressantes?

Harald
 
Es sind 10 "konstante" Variablen, und bisher 5 "flexible".

Das Minimum wird bei 10:10 liegen, der Wunsch so bei 12:15.

Wunsch ist:

Mit dem Minimum bei ca. 20ms Zykluszeit zu landen. Damit könnte ich ziemlich gut leben.

Grüße

Marcel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Mein Tipp: verabschiede Dich von der Idee den Anfang des nächsten Wertes zu suchen. Du beginnst beim Zeichen 280 und konvertierst den ersten Wert bis Du ein Listentrennzeichen findest. Danach geht es genau beim nächsten Zeichen weiter mit dem konvertieren des nächsten Wertes. Nix suchen. Das machst Du durch den ganzen Text, bis Du alles zusammen hast. Da sehe ich nur 1 oder 2 Schleifen pro Wert. Das sollte selbst bei 100 Werten keine 20ms dauern (wenn Du keine Siemens-Bausteine aufrufst). Fertige Bausteine aufrufen mag gut lesbar sein, doch wenn es auf Geschwindigkeit drauf ankommt, dann mache ich meine eigenen auf speed getrimmten Bausteine, die nicht jedesmal jeden pipapo prüfen.

Harald
 
Wie schon einige Beiträge vorher geschrieben,und von dir bestätigt:

Zwischen den Variablen liegt (aus meiner Sicht) Datenmüll mit einer Ebenfalls nicht wirklich definierten länge (da geht es nur um 1-2 Zeichen)

Somit fällt das Flach (ich könnte natürlich vom Ende der Variable + Datenmüllänge die Suche der nächsten Variable einkürzen)

Grüße

Marcel
 
Kurzgefasst - mein Prinzip wäre: nicht etwas suchen, sondern Zeichen für Zeichen durchgehen und schauen, was es kontextabhängig bedeutet. EVA eines Zeichenstromes. Man kann es auch Parser nennen.

Zuerst erwartest Du einen Variablenname, der Dir sagt, wo Du den folgenden Wert ablegen sollst, bis ein '=' kommt.
Dann dürften nur noch Ziffernzeichen kommen, die Du fortlaufend mit der * 10 Geschichte in einen Zwischenwert addierst. Bis ein Dezimalpunkt kommt - aha, es folgen Nachkommaziffern, die ebenfalls fortlaufend "addiert" werden. Oder es kommt gleich oder nach den Nachkommaziffern ein Listentrennzeichen oder Arrayende - die Konvertierung ist beendet, den Zwischenwert nun an die im Varablenname angegebene Variable abspeichern. Wenn noch nicht alle Werte gefunden und kein Arrayende dann sollte nun wieder ein Varablenname für die nächste Variable kommen. Konvertierung von vorne.

Bei diesem Vorgehen ist es egal, ob und wieviele Nachkommastellen die Werte haben. Es sollten allerdings nicht 6 Stellen vor und 6 Stellen nach dem Dezimalpunkt sein - das geht nicht in REAL zu konvertieren.

Harald
 
Zurück
Oben