String zusammen setzen und als Array of Char abspeichern - ohne SCL

saarlaender

Level-1
Beiträge
94
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo nochmal,

ich möchte einen String aus verschiedenen Teilen zusammen bauen und diesen dann "zerhackt" in einem Array of Char abspeichern. (Wie) ist das ohne SCL möglich?

String-Aufbau:
Text_Text_Datum_Uhrzeit_Nummer
Bla1_Bla2_120723_150600_123456

Das String-Zusammenbauen wird wohl über die FC2 CONCAT-Funktion gehen, wenn die mehrmals hintereinander genutzt wird. Den Aufbau von Datum und Uhrzeit kann ich ja über die SFC1 READ_CLK machen, indem ich Date_and_Time erstmal in Temp schreibe und dann über die einzelnen Lokaldaten LB zerlege.

Wie kann ich aber nun die Geschichte in dem DB als Array of Char abspeichern?
 
Hallo,
dein String ist ab der Byte-Position 3 und folgend ein Array_of_Char.
Wenn du dir also den String zusammenbauen kannst ...

Warum aber nicht in SCL ? Darfst du nicht oder willst du nicht ?
SCL ist gerade dafür nämlich schon prädestiniert ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich kann kein SCL :-/

Wie genau das mit dem Zusammensetzen vom String klappt weiß ich noch nicht, das Hauptproblem dürfte aber sein aus Date_and_Time den geforderten String zu basteln.
Hier wollte ich eigentlich über die Lokaldaten gehen und die jeweiligen Daten separat speichern, also z.B. "L LB 10" hat das Jahr und wird über "T Datum_Jahr" (STRING) gespeichert. Das gleiche dann wieder mit Monat, Tag, Stunde, Minute, Sekunde usw... Dann verschwende ich aber extrem viel Speicher, wenn ich da so viele Strings anlegen muss.

Geht das evt. auch irgendwie einfacher bzw. besser ohne SCL?

Wie genau ich nun aber den zusammengesetzten String (dann ab Byte-Position 3) in den DB mit dem Array_of_Char kopiere, ist mir noch nicht klar
 
Zuletzt bearbeitet:
In SCL würde ich es vor Allem deshalb machen, weil es so einen (aus meiner Sicht) wesentlich verständlicheren und durchsichtigeren Code ergibt.
Aus SCL wird nach dem Compilieren natürlich auch ieder AWL und es bedient sich der gleichen Hilfsroutinen (INT_to_String, Concat, Right usw.).
Die hier benötigten Hilfsstrings kannst du natürlich auch alle im TEMP-Bereich deines Bausteins anlegen - erschwert nur ein bißchen das Debugging, was am Anfang ja schon ein Thema ist.

Den fertigen String mußt du nirgendwo anders hinkopieren. Er ist ja schon ein Array_of_Char - nur das er in den ersten beiden Bytes n och zusätzlich die deklarierte Länge und die tatsächliche (also die benutzte) Länge mit drin stehen hat.
Wenn du es aber partout kopieren willst dann mit Blockmove - nur das geht dann (weil nicht in SCL) halt nicht mehr symbolisch ...

Gruß
Larry

Nachsatz:
SCL ist aber auch nicht so schwer zu erlernen ;)
 
SCL werd ich mir sicher auch bald mal antun, aber bis dahin muss ich erstmal drauf verzichten ^^

Also warum ich da nix mehr kopieren muss verstehe ich nich so ganz ^^ aber vielleicht fangen wir mal so an:
-> Wie kann ich erstmal innerhalb einer Funktion einen String wie beschrieben - inkl. Datum/Uhrzeit - zusammen setzen (ohne, dass ich zig String-Variablen anlegen muss)?
Die einzelnen String-Teile kommen (natürlich ohne trennenden "_") von Außen (über IN) sowie durch den SFC1

Dieser String soll dann in einen Array_of_Char eines dynamisch adressierten DBs geschrieben werden.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
OK ... erstmal der Reihe nach ...

Du legst alle deine Teilstrings im TEMP-Bereich deiner FC (oder deines FB) an. Da kommst du auch nicht drum herum - nur das sie dich im TEMP-Bereich ja erstmal nicht belasten. Du mußt sie ja auch nicht größer machen als unbedingt nötig.
Nun mußt du bei jedem der Teilstrings den Header initialisieren - also deklarierte Länge im richtigen Header-Byte eintragen. (das brauchen die Siemens-FC's).
Nun liest du dir z.B. die Stunde aus deinem DT-Byte-Array heraus, addierst 100 da darauf (damit du für z.B. 9 Uhr später 09 Uhr erhalten könntest), wandelst das mit String_to_INT (den FC-Namen weiß ich gerade nicht) in einen String um und schneidest mit Right (auch wieder die Sache mit dem FC-Namen) die rechten 2 Stellen ab und schreibst das dann in deinen Teilstring "Stunde".
Genau so verfährst du dann auch mit allen anderen Einzelinformationen aus denen erst noch ein formatierter String werden will.
Alle deine Teilstrings schiebst du hinterher mit Concat zu dem "großen" Endstring zusammen - der kann dann in deinem DB stehen.

Das ganze kann man natürlich hinterher noch ein bißchen Platz-sparender machen - ich denke aber, zuerst kommt erstmal die Funktion und das Verstehen derselben ...

Kommst du damit erstmal an den Start ?

Gruß
Larry
 
Also wenn ich n String anlege, is der ja immer gleich groß - da kann ich ja dann nicht viel sparen.
Bei der Uhrzeit meinst du evt. eher Int_to_String statt String_to_Int?!


Also konkret:
ich hau mir etliche Strings rein, also z.B. fürs Datum 3, für die Uhrzeit 3, für den jeweiligen Rest jeweils einen und dann je nachdem evt. noch für jeden trennenden "_". Die Datum-Uhrzeit-Geschichte wie angegeben.
Danach dann alle Teilstrings zu einem String zusammenfassen.

Den "Endstring" kann ich dann z.B. über BLKMOV in den Array_of_Char kopieren? Wie adressiere ich dann als BLKMOV-Quelle den in TEMP liegenden "Endstring" so, dass erst ab Byte 3 kopiert wird?
... und wie genau initialisiere ich die Header der Teilstrings genau?


Ansonsten dürfts klar sein ;) thx schonmal
 
Strings

Moin,
ich habe ein kleines Bspl. (Auszug aus einem FB) für Stringverarbeitung im TEMP Bereich. Die Strings hier werden z.T. 'zusammengebastelt'
und per Ethernet an ein externes Gerät gesendet.
Im TEMP Bereich sind die jeweiligen Strings mit Größe angelegt z.B. WZS_Rstrg String[14]

Wichtig is hier, dass die maximale Stringgrösse (Byte0) im Header vorbelegt wird (in STAT muss das nicht erfolgen), im Byte 1 findest du die aktuelle Länge.

Evtl. kannst du ja etwas daraus für deinen Lösungsansatz interpretieren.


Code:
// +++ Temporäre Stringgrösse vorbelegen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##WZS_RStrg                // WZS-Realstring hat
      L     14                          // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 14


      LAR1  P##ZMI_IStrg                // ZMI-Integerstring hat
      L     5                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 5


      LAR1  P##ZMI_Strg                 // ZMI-MID-String hat
      L     4                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 4


      LAR1  P##TBU_IStrg                // TBU-Integerstring hat
      L     3                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 3


      LAR1  P##TBU_Strg                 // TBU-MID-String hat
      L     2                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 2


      LAR1  P##WZS                      // WZS Sendestring hat
      L     5                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 5


      LAR1  P##ZMI                      // ZMI Sendestring hat
      L     4                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 4
      T     LB [AR1,P#1.0]              // *** Istgrösse 4


      LAR1  P##TBU                      // TBU Sendestring hat
      L     2                           // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 2
      T     LB [AR1,P#1.0]              // *** Istgrösse 2


      LAR1  P##ZDWoK                    // ZDW ohne Kommastellestring hat
      L     20                          // 
      T     LB [AR1,P#0.0]              // *** Stringgrösse 20



// +++ WZ-Wert als String aufbereiten +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      CALL  "R_STRNG"                   // 
       IN     :=#WZS_Real               // 
       RET_VAL:=#WZS_RStrg              // *** Wandel REAL-Wert als String (±v.nnnnnnnE±xx)


      CALL  "MID"                       //  
       IN     :=#WZS_RStrg              // 
       L      :=5                       // es werden 5 Stellen benötigt
       P      :=2                       // ab Position 2 (1. ist Vorzeichen)
       RET_VAL:=#WZS                    // *** Wandel REAL-String zum Sollwert-String (v.nnn)


// +++ ZMI-Wert als String aufbereiten ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##ZMI                      // Pointer auf ZMI setzen
      L     '0000'                      // 
      T     LD [AR1,P#2.0]              // *** ZMI - Wert auf '0000' initialisieren


      CALL  "I_STRNG"                   // 
       I      :=#ZMI_INT                // 
       RET_VAL:=#ZMI_IStrg              // *** Wandel INT-Wert als String (±nnnn)


      CALL  "MID"                       //  
       IN     :=#ZMI_IStrg              // 
       L      :=4                       // es werden 4 Stellen benötigt
       P      :=2                       // ab Position 2 (1. ist Vorzeichen)
       RET_VAL:=#ZMI_Strg               // *** Wandel INT-String zum Sollwert-String (nnnn)


      LAR1  P##ZMI_Strg                 // Pointer auf ZMI-String setzen
      L     4                           // String hat maximal 4 Zeichen
      L     LB [AR1,P#1.0]              // 
      T     #StrgLaenge                 // *** Stringlänge auslesen
      +     -1                          // 
      -I                                // 
      T     #StrgPos                    // *** Stringposition 1. Zeichen


      CALL  "REPLACE"                   // Zementgewicht rechtsbündig im String ausrichten
       IN1    :=#ZMI                    // 
       IN2    :=#ZMI_Strg               // 
       L      :=#StrgLaenge             // 
       P      :=#StrgPos                // 
       RET_VAL:=#ZMI                    // *** ZMI Wert im richtigen Format


// +++ TBU als String aufbereiten ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##TBU                      // Pointer auf TBU setzen
      L     '00'                        // 
      T     LW [AR1,P#2.0]              // TBU - Wert auf '00' initialisieren


      CALL  "I_STRNG"                   // 
       I      :=#BTU_INT                // 
       RET_VAL:=#TBU_IStrg              // *** Wandel INT-Wert als String (±nnnn)


      CALL  "MID"                       //  
       IN     :=#TBU_IStrg              // 
       L      :=2                       // es werden 2 Stellen benötigt
       P      :=2                       // ab Position 2 (1. ist Vorzeichen)
       RET_VAL:=#TBU_Strg               // *** Wandel INT-String zum Sollwert-String (nnnn)


      LAR1  P##TBU_Strg                 // Pointer auf TBU-String setzen 
      L     2                           // String hat maximal 4 Zeichen
      L     LB [AR1,P#1.0]              // 
      T     #StrgLaenge                 // *** Stringlänge auslesen
      +     -1                          // 
      -I                                // 
      T     #StrgPos                    // *** Stringposition 1. Zeichen


      CALL  "REPLACE"                   // Eichkurve rechtsbündig im String ausrichten
       IN1    :=#TBU                    // 
       IN2    :=#TBU_Strg               // 
       L      :=#StrgLaenge             // 
       P      :=#StrgPos                // 
       RET_VAL:=#TBU                    // *** TBU Wert im richtigen Format
 

// +++ Schreibe WZS ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##WZS                      // Pointer auf WZS 
      L     LB [AR1,P#2.0]              // 
      T     #Send.WZS.Zpxxx             // *** Schreibe Wert X.xxx
      L     LB [AR1,P#3.0]              // 
      T     #Send.WZS.xPxxx             // *** Schreibe Pnkt xPxxx
      L     LB [AR1,P#4.0]              // 
      T     #Send.WZS.xpZxx             // *** Schreibe Wert x.Xxx
      L     LB [AR1,P#5.0]              // 
      T     #Send.WZS.xpxZx             // *** Schreibe Wert x.xXx
      L     LB [AR1,P#6.0]              // 
      T     #Send.WZS.xpxxZ             // *** Schreibe Wert x.xxX


// +++ ZMI Sollwert ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##ZMI                      // Pointer auf ZMI
      L     LB [AR1,P#2.0]              // 
      T     #Send.ZMI.Txxx              // *** Schreibe Wert Xxxx
      L     LB [AR1,P#3.0]              // 
      T     #Send.ZMI.xHxx              // *** Schreibe Wert xXxx
      L     LB [AR1,P#4.0]              // 
      T     #Send.ZMI.xxZx              // *** Schreibe Wert xxXx
      L     LB [AR1,P#5.0]              // 
      T     #Send.ZMI.xxxE              // *** Schreibe Wert xxxX


// +++ TBU ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


      LAR1  P##TBU                      // Pointer auf TBU
      L     LB [AR1,P#2.0]              // 
      T     #Send.TBU.Zx                // *** Schreibe Wert Xx
      L     LB [AR1,P#3.0]              // 
      T     #Send.TBU.xE                // *** Schreibe Wert xX
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich denke der Code von Ingo veranschaulicht das, was ich meinte schon ganz gut.
Bei der Wandlung hattest du natürlich Recht : es mußt INT_to_String sein - Logisch ... sorry dafür.

Für den anschließenden (finalen) Blockmove machst du dir einen ANY-Pointer, der auf Endstring-Anfang +2 zeigt und entsprechend die Information über die Anzahl der verwendeten Zeichen beinhaltet.
Das ist aber m.E. ein eigenes Thema ...

Gruß
Larry
 
Also wenn ich das richtig kapiert hab, würde folgendes klappen:

1. in Temp (oder Stat, dann brauchts keine Angabe der Stringlänge) die String-Variablen anlegen. Maximale Länge kann man definieren mit String[XX]

Der String-Header würde bei 2 Zeichen wie folgt belegt werden:
LAR1 P##Stunden
L 2
T LB [AR1,P#0.0]

2. Aus meiner SFC1-Variable (TEMP; Date_and_Time - ab 10.0) z.B. über L LB 13 die Stunden auslesen und z.B. in einer I_Stunden (Temp; Integer) zwischenspeichern

3. Mit FC16 I_STRNG (Inteter->String) die I_Stunden umwandeln und z.B. als Str_Stunden (Temp; String[2], "String-Header" wie oben vorbelegt) speichern

...

Am Ende dann mit FC2 Concat die einzelnen Strings zusammen basteln und mit Blockmove z.B. in den DB verschieben (hier dann ohne String-HEader). Im DB kann man dann das Array_of_Char direkt an Blockmove hängen und somit beschreiben.


Soweit korrekt?
 
:) ... fast ...

Du hast vergessen, dass du als Uhrzeit z.B. ja 9:07 Uhr haben könntest. Wenn du das dann als 0907 in deinen Ziel-String packen willst mußt du erst noch zusehen, dass du in deinen Integer noch die führende Null mit hineinbringst.
Deshalb hatte ich vorgeschlagen, auf z.B. die Stunde 100 aufzuaddieren, das zu einem String zu machen und von diesem mit Int_to_String gewandelten Wert nur die rechten beiden Zeichen zu verwenden (String-Funktion RIGHT).

Das kannst du jetzt natürlich in gleicher Weise auf jeden Teil-Betrag der Uhrzeit oder auch des Datums übertragen ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich bin leider erst jetzt wieder dazu gekommen ... irgendwo hab ich wohl noch einen Fehler drin.


Aktuell hab ich folgendes umgesetzt:

In Temp sind die einzelnen Strings drin..
3x String[2] für die Stunden, Minuten, Sekunden
1x String[4] für den String Stunden_Minuten
2x String[6] für den String mit Stunden, Minuten, Sekunden

Dann die Zerlegung von DATE_AND_TIME und Vorbelegung der Strings in TEMP:

Code:
//Date_And_Time auslesen und zerlegen
//Stunden (LB13)
      L     LB    13
      T     #Stunden_I
//Minuten (LB14)
      L     LB    14
      T     #Minuten_I
//Sekunden (LB15)
      L     LB    15
      T     #Sekunden_I
//;
//String-Header vorbelegen
//Stunden
      LAR1  P##Stunden_Str
      L     2
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]
//Minuten
      LAR1  P##Minuten_Str
      L     2
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]
//Sekunden
      LAR1  P##Sekunden_Str
      L     2
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]

//Stunden_Minuten
      LAR1  P##Stunden_Minuten_Str
      L     4
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]
//Komplett_Str_1
      LAR1  P##Komplett_Str_1
      L     6
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]
//Komplett_Str_2
      LAR1  P##Komplett_Str_2
      L     6
      T     LB [AR1,P#0.0]
      T     LB [AR1,P#2.0]

//;

Anschließend 3x FC16 I_STRNG (Int2String) - hier wird aus den Stunden_I, Minuten_I, Sekunden_I jeweils der String generiert (Stunden_Str, Minuten_Str, Sekunden_Str).

Danach 2x FC2 CONCAT:
#1
In1: Stunden_Str
In2: Minuten_Str
RET_VAL: Stunden_Minuten_Str

#2
In1: Sekunden_Str
In2: Stunden_Minuten_Str
RET_VAL: Komplett_Str_1


Mit FC32 (RIGHT) will ich mir die letzten 6 Zeichen schnappen (hatte gerade keinen Ansatz, wie ich mir den passenden Pointer schnell zusammen basteln kann) - also ohne die 2 Byte am Anfang vom String.
also:
In: Komplett_Str_1
L: 6
RET_VAL: Komplett_Str_2


Anschließend SFC20 (BLKMOV) mit
SRCBLK: Komplett_Str_2
DSTBLK: P#DB50.DBX0.0 Byte 39



Ich bekomme auch tatsächlich Zeichen in den DB, allerdings keine Zahlen sondern Sonderzeichen in Strichform


Woooo liegt der Fehler? :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Mit FC32 (RIGHT) will ich mir die letzten 6 Zeichen schnappen (hatte gerade keinen Ansatz, wie ich mir den passenden Pointer schnell zusammen basteln kann) - also ohne die 2 Byte am Anfang vom String.
also:
In: Komplett_Str_1
L: 6
RET_VAL: Komplett_Str_2


Anschließend SFC20 (BLKMOV) mit
SRCBLK: Komplett_Str_2
DSTBLK: P#DB50.DBX0.0 Byte 39

... ansonsten (als Add-on zu Volkers Beitrag) :
mit den String-Funktionen kannst du nicht die Stringdaten vom String-Header trennen - da kommt dann trotzdem immer wieder ein String (mit Header zurück) ...
Einfach einen Pointer auf den String bilden und den dann um 2 Byte erhöhen, daraus einen ANY-Pointer bilden und den dann an den Blockmove übergeben ...

Gruß
Larry
 
... ich bin gerade mal dabei - dann mache ich das mal für Volker :
Code:
//Date_And_Time auslesen und zerlegen
//Stunden (LB13)
      L     LB    13
BTI
      T     #Stunden_I
//Minuten (LB14)
      L     LB    14
BTI
      T     #Minuten_I
//Sekunden (LB15)
      L     LB    15
BTI
      T     #Sekunden_I
...

Nachsatz:
Ob du da die richtigen LB's verwendet hast habe ich jetzt nicht überprüft ...
 
Thx, dachte mir schon dass es zu Date_And_Time gehören muss, aber hab jetzt erstmal ne BTI-Funktion gesucht ^^

wg. dem Pointer: Ich versteh den Pointer-Kram zwar einigermaßen, weiß aber jetzt gerade nich wie ich mir den Pointer dazu so baue dass er passt. Daher wollte ich RIGHT benutzen oder auch MID evt.



EDIT: Auch wenn ich BTI einbaue, kommen am Ende nur komische Striche bei raus...

EDIT2: Die LBs sind richtig, ich seh vor dem BTI auch die richtige Zeit und die Sekunden hochlaufen

EDIT3: in DEZ bekomme ich
DBB0: 6
DBB1: 2
DBB2: 2
DBB3: 57


Die Werte ändern sich interessanterweise nicht ständig sondern nur die ersten 10 Sekunden einer Minute
 
Zuletzt bearbeitet:
Ähh ... DBW 1,2,3,4 ?????
DBW's bitte nur in 2er Schritten hochzählen, weil z.B. das DBW0 das DBB0 und 1 beinhaltet, DBW1 das DBB1 und 2 usw.
Ansonsten sollte es dann passen ...
 
Zurück
Oben