Step 7 Date and Time 2 String

soundmachine123

Level-1
Beiträge
102
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

wie der Titel schon sagt stehe ich vor folgendem Problem:

Ich lese zyklisch die aktuelle SPS Zeit aus und muss diese mit vielen weiteren Telemetriedaten als Zeitstempel in einem DB ablegen. Von dort werden die Daten dann von einem Leitwarten-PC abgeholt.
Datum un Uhrzeit muss ich in zwei getrennten strings ablegen.
Date and Time in zwei einzelne Variablen (Date und Time of Day) auftrennen ist ja kein Ding, aber wie bekomme ich die Wandlung zum jeweiligen string hin?
Hat hier jemand vielleicht eine Ídee oder sogar einen fertigen Wandlungsbaustein?
Aufgrund immensem Zeitdruck bin ich für jede Hilfe dankbar.
 
Function DT_STRNG in SCL

Einen fertigen Datum/Uhrzeit-String-Formatierer gibt es anscheinend nicht, die Aufgabe ist aber einfach zu lösen. Man muß nur die ersten 6 Byte eines DATE_AND_TIME in 12 Ziffern (CHAR) umwandeln und dann in der gewünschten Reihenfolge nebst Trennzeichen in die Ausgabe-Strings kopieren.

Eine effiziente BCD(Hex)-zu-CHAR-Wandlung kann man in AWL schnell "zu Fuß" programmieren, man kann aber auch den FC95 HTA dafür nehmen. Da man für den Zugriff auf die zusammengesetzten Datentypen DATE_AND_TIME und STRING an FC-IN und FC-OUT indirekte Adressierung braucht, ist das ganze aber besser lesbar und schneller getippt in SCL zu lösen.

Hier eine Variante in SCL welche ohne weitere Bausteine auskommt
Code:
FUNCTION DT_STRNG : VOID
TITLE='DATE_AND_TIME in Datum-String und Uhrzeit-String konvertieren'
VERSION : '1.0'
AUTHOR  : PN_DP
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    D : STRING[10] ; // String für Datum '31.12.2089'
    baD AT D : ARRAY[-1..10] OF BYTE ;
    T : STRING[8] ;  // String für Uhrzeit '23:59:59'
    baT AT T : ARRAY[-1..8] OF BYTE ;
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    baDT AT tmpDT : STRUCT
        Year   : BYTE ;  // Jahr    BCD (19)90..(20)89
        Month  : BYTE ;  // Monat   BCD 01..12
        Day    : BYTE ;  // Tag     BCD 01..31
        Hour   : BYTE ;  // Stunde  BCD 00..23
        Minute : BYTE ;  // Minute  BCD 00..59
        Second : BYTE ;  // Sekunde BCD 00..59
        msMSD  : BYTE ;
        wDay_msLSD : BYTE ;
    END_STRUCT ;

    tmpD : STRING[10] ; // String für Datum '31.12.2089'
    tbaD AT tmpD : ARRAY[-1..10] OF BYTE ;

    tmpT : STRING[8] ;  // String für Uhrzeit '23:59:59'
    tbaT AT tmpT : ARRAY[-1..8] OF BYTE ;

    tmpWord : WORD ;
    tmpW AT tmpWord : STRUCT
        B1 : BYTE ;
        B0 : BYTE ;
    END_STRUCT ;
END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN ;

    // Datum --> '31.12.2089'
    tmpD := '00.00.2000' ; // String initialisieren

    tmpW.B0 := baDT.Day ; // Tag
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Day), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[1] := tmpW.B1 ;
    tbaD[2] := tmpW.B0 ;

    tmpW.B0 := baDT.Month ; // Monat
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Month), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[4] := tmpW.B1 ;
    tbaD[5] := tmpW.B0 ;

    tmpW.B0 := baDT.Year ; // Jahr
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Year), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[9] := tmpW.B1 ;
    tbaD[10] := tmpW.B0 ;

    baD := tbaD ; // Datum-String nach OUTPUT kopieren

    // Uhrzeit --> '23:59:59'
    tmpT := '00:00:00' ; // String initialisieren

    tmpW.B0 := baDT.Hour ; // Stunde
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Hour), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[1] := tmpW.B1 ;
    tbaT[2] := tmpW.B0 ;

    tmpW.B0 := baDT.Minute ; // Minute
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Minute), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[4] := tmpW.B1 ;
    tbaT[5] := tmpW.B0 ;

    tmpW.B0 := baDT.Second ; // Sekunde
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Second), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[7] := tmpW.B1 ;
    tbaT[8] := tmpW.B0 ;

    baT := tbaT ; // Uhrzeit-String nach OUTPUT kopieren

END_FUNCTION

// Optimierungs-Unterstützung des SCL-Compilers (K05.03.05.00_01.03.00.01):
// - direktes Bearbeiten der IN- und OUT-Parameter benötigt ca. doppelt soviel Programmgröße wie Bearbeiten in TEMP
// - SHR(IN:=ByteVar, N:=4) benötigt 22 Byte mehr als DWORD_TO_BYTE(SHR(IN:=BYTE_TO_DWORD(ByteVar), N:=4))
// - Division durch 16 statt SHR 4 oder nicht Zwischenspeichern in tmpWord benötigen geringfügig mehr
// - (T := tmpT) String-Kopieren benötigt fast 200 Byte mehr als Array kopieren (baT := tbaT)

Und eine zweite Variante in SCL, welche den FC95 HTA aus den TI-S7 Converting Blocks nutzt.
Sieht kürzer aus, braucht aber mit dem FC HTA zusammen etwas mehr Programmspeicher.
Code:
FUNCTION DT_STRNG2 : VOID
TITLE='DATE_AND_TIME in Strings konvertieren'
VERSION : '1.0'
AUTHOR  : PN_DP
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    D : STRING[10] ; // String für Datum '31.12.2089'
    baD AT D : ARRAY[-1..10] OF BYTE ;
    T : STRING[8] ;  // String für Uhrzeit '23:59:59'
    baT AT T : ARRAY[-1..8] OF BYTE ;
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    tmpArray : ARRAY[1..12] OF BYTE ;
END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN ;
    // und mit FC HTA in CHARs wandeln --> 'YYMMDDHHMMSS'
    HTA(IN:=tmpDT, N:=6, OUT:=tmpArray);

    // Datum --> '31.12.2089'
    baD[-1] := 10 ; // max Stringlänge
    baD[0] := 10 ; // akt. Stringlänge
    baD[1] := tmpArray[5] ; // Tag
    baD[2] := tmpArray[6] ;
    baD[3] := CHAR_TO_BYTE('.') ;
    baD[4] := tmpArray[3] ; // Monat
    baD[5] := tmpArray[4] ;
    baD[6] := CHAR_TO_BYTE('.') ;
    baD[7] := CHAR_TO_BYTE('2') ;
    baD[8] := CHAR_TO_BYTE('0') ;
    baD[9] := tmpArray[1] ; // Jahr
    baD[10] := tmpArray[2] ;

    // Uhrzeit --> '23:59:59'
    baT[-1] := 8 ; // max Stringlänge
    baT[0] := 8 ; // akt. Stringlänge
    baT[1] := tmpArray[7] ; // Stunde
    baT[2] := tmpArray[8] ;
    baT[3] := CHAR_TO_BYTE(':') ;
    baT[4] := tmpArray[9] ; // Minute
    baT[5] := tmpArray[10] ;
    baT[6] := CHAR_TO_BYTE(':') ;
    baT[7] := tmpArray[11] ; // Sekunde
    baT[8] := tmpArray[12] ;

END_FUNCTION

Beide Varianten funktionieren korrekt nur im Datumsbereich 01.01.2000 bis 31.12.2089, weil die zweistellige Jahresangabe des DATE_AND_TIME einfach immer zu 20xx erweitert wird!

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich muss mal nochmals dieses Thema ausgraben.

Ich wollte das obere ohne FC HTA Beispiel von PN/DP ausprobieren, da ich das Datum und die Zeit in einem String brauche.
Also das Beispiel kopiert und in eine Textdatei mit Endung .scl gespeichert. Danach im Tia (V13SP1Upd2) als externe Quelle einfügen und dann Baustein generieren. Allerdings nach Baustein generieren aus Quelle blinkt mir der Baustein nur kurz auf und verschwindet wieder!

Also von Hand die Dekleration machen. --> Weiteres Problem: tmpW.B1 intepretiert TIA als slicezugriff auf ein Byte. Also noch ein Q vor B (Dekleration und Programm) geschrieben. Allerdings geht es mir auch so nicht.
Ich denke mein Problem ist die Dekleration: es hat ein paar Mal ein AT drin?! Was heisst das? Z.B
Code:
tmpDT : DATE_AND_TIME ;
    baDT AT tmpDT : STRUCT
        Year   : BYTE ;  // Jahr    BCD (19)90..(20)89
        Month  : BYTE ;  // Monat   BCD 01..12
        Day    : BYTE ;  // Tag     BCD 01..31
        Hour   : BYTE ;  // Stunde  BCD 00..23
        Minute : BYTE ;  // Minute  BCD 00..59
        Second : BYTE ;  // Sekunde BCD 00..59
        msMSD  : BYTE ;
        wDay_msLSD : BYTE ;
    END_STRUCT ;

Gruss blimaa



Edit: OK hab die AT Ansicht hinbekommen und es arbeitet wie gewollt. Allerdings habe ich keine Ahnung was die AT Ansicht genau ist :rolleyes::sm16:
 
Zuletzt bearbeitet:
Einen fertigen Datum/Uhrzeit-String-Formatierer gibt es anscheinend nicht, die Aufgabe ist aber einfach zu lösen. Man muß nur die ersten 6 Byte eines DATE_AND_TIME in 12 Ziffern (CHAR) umwandeln und dann in der gewünschten Reihenfolge nebst Trennzeichen in die Ausgabe-Strings kopieren.

Eine effiziente BCD(Hex)-zu-CHAR-Wandlung kann man in AWL schnell "zu Fuß" programmieren, man kann aber auch den FC95 HTA dafür nehmen. Da man für den Zugriff auf die zusammengesetzten Datentypen DATE_AND_TIME und STRING an FC-IN und FC-OUT indirekte Adressierung braucht, ist das ganze aber besser lesbar und schneller getippt in SCL zu lösen.

Hier eine Variante in SCL welche ohne weitere Bausteine auskommt
Code:
FUNCTION DT_STRNG : VOID
TITLE='DATE_AND_TIME in Datum-String und Uhrzeit-String konvertieren'
VERSION : '1.0'
AUTHOR  : PN_DP
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    D : STRING[10] ; // String für Datum '31.12.2089'
    baD AT D : ARRAY[-1..10] OF BYTE ;
    T : STRING[8] ;  // String für Uhrzeit '23:59:59'
    baT AT T : ARRAY[-1..8] OF BYTE ;
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    baDT AT tmpDT : STRUCT
        Year   : BYTE ;  // Jahr    BCD (19)90..(20)89
        Month  : BYTE ;  // Monat   BCD 01..12
        Day    : BYTE ;  // Tag     BCD 01..31
        Hour   : BYTE ;  // Stunde  BCD 00..23
        Minute : BYTE ;  // Minute  BCD 00..59
        Second : BYTE ;  // Sekunde BCD 00..59
        msMSD  : BYTE ;
        wDay_msLSD : BYTE ;
    END_STRUCT ;

    tmpD : STRING[10] ; // String für Datum '31.12.2089'
    tbaD AT tmpD : ARRAY[-1..10] OF BYTE ;

    tmpT : STRING[8] ;  // String für Uhrzeit '23:59:59'
    tbaT AT tmpT : ARRAY[-1..8] OF BYTE ;

    tmpWord : WORD ;
    tmpW AT tmpWord : STRUCT
        B1 : BYTE ;
        B0 : BYTE ;
    END_STRUCT ;
END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN ;

    // Datum --> '31.12.2089'
    tmpD := '00.00.2000' ; // String initialisieren

    tmpW.B0 := baDT.Day ; // Tag
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Day), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[1] := tmpW.B1 ;
    tbaD[2] := tmpW.B0 ;

    tmpW.B0 := baDT.Month ; // Monat
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Month), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[4] := tmpW.B1 ;
    tbaD[5] := tmpW.B0 ;

    tmpW.B0 := baDT.Year ; // Jahr
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Year), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaD[9] := tmpW.B1 ;
    tbaD[10] := tmpW.B0 ;

    baD := tbaD ; // Datum-String nach OUTPUT kopieren

    // Uhrzeit --> '23:59:59'
    tmpT := '00:00:00' ; // String initialisieren

    tmpW.B0 := baDT.Hour ; // Stunde
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Hour), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[1] := tmpW.B1 ;
    tbaT[2] := tmpW.B0 ;

    tmpW.B0 := baDT.Minute ; // Minute
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Minute), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[4] := tmpW.B1 ;
    tbaT[5] := tmpW.B0 ;

    tmpW.B0 := baDT.Second ; // Sekunde
    tmpW.B1 := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Second), N:=4) ) ;
    tmpWord := tmpWord AND W#16#0F0F OR W#16#3030 ;
    tbaT[7] := tmpW.B1 ;
    tbaT[8] := tmpW.B0 ;

    baT := tbaT ; // Uhrzeit-String nach OUTPUT kopieren

END_FUNCTION

// Optimierungs-Unterstützung des SCL-Compilers (K05.03.05.00_01.03.00.01):
// - direktes Bearbeiten der IN- und OUT-Parameter benötigt ca. doppelt soviel Programmgröße wie Bearbeiten in TEMP
// - SHR(IN:=ByteVar, N:=4) benötigt 22 Byte mehr als DWORD_TO_BYTE(SHR(IN:=BYTE_TO_DWORD(ByteVar), N:=4))
// - Division durch 16 statt SHR 4 oder nicht Zwischenspeichern in tmpWord benötigen geringfügig mehr
// - (T := tmpT) String-Kopieren benötigt fast 200 Byte mehr als Array kopieren (baT := tbaT)

Und eine zweite Variante in SCL, welche den FC95 HTA aus den TI-S7 Converting Blocks nutzt.
Sieht kürzer aus, braucht aber mit dem FC HTA zusammen etwas mehr Programmspeicher.
Code:
FUNCTION DT_STRNG2 : VOID
TITLE='DATE_AND_TIME in Strings konvertieren'
VERSION : '1.0'
AUTHOR  : PN_DP
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    D : STRING[10] ; // String für Datum '31.12.2089'
    baD AT D : ARRAY[-1..10] OF BYTE ;
    T : STRING[8] ;  // String für Uhrzeit '23:59:59'
    baT AT T : ARRAY[-1..8] OF BYTE ;
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    tmpArray : ARRAY[1..12] OF BYTE ;
END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN ;
    // und mit FC HTA in CHARs wandeln --> 'YYMMDDHHMMSS'
    HTA(IN:=tmpDT, N:=6, OUT:=tmpArray);

    // Datum --> '31.12.2089'
    baD[-1] := 10 ; // max Stringlänge
    baD[0] := 10 ; // akt. Stringlänge
    baD[1] := tmpArray[5] ; // Tag
    baD[2] := tmpArray[6] ;
    baD[3] := CHAR_TO_BYTE('.') ;
    baD[4] := tmpArray[3] ; // Monat
    baD[5] := tmpArray[4] ;
    baD[6] := CHAR_TO_BYTE('.') ;
    baD[7] := CHAR_TO_BYTE('2') ;
    baD[8] := CHAR_TO_BYTE('0') ;
    baD[9] := tmpArray[1] ; // Jahr
    baD[10] := tmpArray[2] ;

    // Uhrzeit --> '23:59:59'
    baT[-1] := 8 ; // max Stringlänge
    baT[0] := 8 ; // akt. Stringlänge
    baT[1] := tmpArray[7] ; // Stunde
    baT[2] := tmpArray[8] ;
    baT[3] := CHAR_TO_BYTE(':') ;
    baT[4] := tmpArray[9] ; // Minute
    baT[5] := tmpArray[10] ;
    baT[6] := CHAR_TO_BYTE(':') ;
    baT[7] := tmpArray[11] ; // Sekunde
    baT[8] := tmpArray[12] ;

END_FUNCTION

Beide Varianten funktionieren korrekt nur im Datumsbereich 01.01.2000 bis 31.12.2089, weil die zweistellige Jahresangabe des DATE_AND_TIME einfach immer zu 20xx erweitert wird!

Harald

Danke dir für die SCL-Funktion.
Genau danach habe ich heute gesucht :cool:.
 
So geht's auch!
hier das Beispiel von PN_DP nochmals etwas modifiziert
und BLKMOV für das Kopieren des ByteArray zu String verwendet.
Stringlänge wird dabei automatisch von BLKMOV eingetragen.
Jahr 19xx bzw 20xx ergänzt

Code:
FUNCTION DT_STRNG : VOID
TITLE='DATE_AND_TIME in Datum-String und Uhrzeit-String konvertieren'
VERSION : '1.0'
AUTHOR  : SM
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    strD : STRING[10] ; // String für Datum '31.12.2089'
    strT : STRING[8] ;  // String für Uhrzeit '23:59:59'
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    baDT AT tmpDT : STRUCT
        Year   : BYTE ;  // Jahr    BCD (19)90..(20)89
        Month  : BYTE ;  // Monat   BCD 01..12
        Day    : BYTE ;  // Tag     BCD 01..31
        Hour   : BYTE ;  // Stunde  BCD 00..23
        Minute : BYTE ;  // Minute  BCD 00..59
        Second : BYTE ;  // Sekunde BCD 00..59
        msMSD  : BYTE ;
        wDay_msLSD : BYTE ;
    END_STRUCT ;

    tbaD : ARRAY[1..10] OF BYTE ;
    tbaT : ARRAY[1..8] OF BYTE ;
    
    iRet : INT;

END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN ;

    tbaD[3] := CHAR_TO_BYTE('.');
    tbaD[6] := CHAR_TO_BYTE('.');
    
    // TAG
    tbaD[1] := SHR(IN:=baDT.Day, N:=4) OR B#16#30;
    tbaD[2] := baDT.Day AND B#16#0F OR B#16#30;

    // Monat 
    tbaD[4] := SHR(IN:=baDT.Month, N:=4) OR B#16#30;
    tbaD[5] := baDT.Month AND B#16#0F OR B#16#30;
    
    // Jahr
    tbaD[9] :=  SHR(IN:=baDT.Year, N:=4) OR B#16#30  ;
    tbaD[10] := baDT.Year AND B#16#0F OR B#16#30;

    IF tbaD[9] = CHAR_TO_BYTE('9') THEN
       tbaD[7] := CHAR_TO_BYTE('1');      // 19 für 1990..1999
       tbaD[8] := CHAR_TO_BYTE('9');
    ELSE
       tbaD[7] := CHAR_TO_BYTE('2');      // 20 für 2000..2089
       tbaD[8] := CHAR_TO_BYTE('0');
    END_IF;

    tbaT[3] := CHAR_TO_BYTE(':');
    tbaT[6] := CHAR_TO_BYTE(':');

    // Stunde
    tbaT[1] := SHR(IN:=baDT.Hour, N:=4) OR B#16#30;
    tbaT[2] := baDT.Hour AND B#16#0F OR B#16#30;
    
    // Minute
    tbaT[4] := SHR(IN:=baDT.Minute, N:=4) OR B#16#30;
    tbaT[5] := baDT.Minute AND B#16#0F OR B#16#30;

    // Sekunde
    tbaT[7] := SHR(IN:=baDT.Second, N:=4)OR B#16#30;
    tbaT[8] := baDT.Second AND B#16#0F OR B#16#30;
 
    // ACHTUNG: BLKMOV kopiert alle Bytes des Array und
    // trägt dann automatisch im String die korrekte Länge ein!
    
    // ByteArray Datum =>  D: String
    iRet := BLKMOV(SRCBLK := tbaD, DSTBLK := strD);

    // ByteArray Time =>  T: String
    iRet := BLKMOV(SRCBLK := tbaT, DSTBLK := strT);

   
END_FUNCTION
 
Zuviel Werbung?
-> Hier kostenlos registrieren
OK, wenn man jedes Zeichen einzeln behandelt dann sieht der Code übersichtlicher aus.
Doch die Idee, den Compiler zu BLKMOV zu zwingen ist eher nicht so gut.

In SCL sollte es nicht nötig sein, explizit BLKMOV aufzurufen. Das kann man dem Compiler überlassen, ob er BLKMOV einsetzt oder die paar Bytes gleich direkt kopiert. Schon alleine das Zusammenbasteln der 2 ANY für BLKMOV macht mindestens soviel Aufwand wie die 12 + 10 Bytes direkt mit L/T zu kopieren. Und da hat BLKMOV noch nichtmal angefangen ... Und falls man den Code mal zu TIA migrieren muß bekommt man zusätzliche Probleme wenn man explizit BLKMOV verwendet hat.

Codegröße mit BLKMOV: 708 Bytes
ohne BLKMOV formuliert: 678 Bytes
Dann noch alle SHR "compilerfreundlich" als DWORD formuliert: 546 Bytes
Code:
FUNCTION DT_STRNG : VOID
TITLE='DATE_AND_TIME in Datum-String und Uhrzeit-String konvertieren'
VERSION : '1.1'
AUTHOR  : PN_DP
NAME    : DT_STRNG
FAMILY  : CONVERT

VAR_INPUT
    IN : DATE_AND_TIME ;
END_VAR

VAR_OUTPUT
    strD : STRING[10] ; // String für Datum '31.12.2089'
        baDout AT strD : ARRAY[-1..10] OF BYTE ;
    strT : STRING[8] ;  // String für Uhrzeit '23:59:59'
        baTout AT strT : ARRAY[-1..8] OF BYTE ;
END_VAR

VAR_TEMP
    tmpDT : DATE_AND_TIME ;
    baDT AT tmpDT : STRUCT
        Year   : BYTE ;  // Jahr    BCD (19)90..(20)89
        Month  : BYTE ;  // Monat   BCD 01..12
        Day    : BYTE ;  // Tag     BCD 01..31
        Hour   : BYTE ;  // Stunde  BCD 00..23
        Minute : BYTE ;  // Minute  BCD 00..59
        Second : BYTE ;  // Sekunde BCD 00..59
        msMSD  : BYTE ;
        wDay_msLSD : BYTE ;
    END_STRUCT ;

    tmpD : STRING[10] ; // String für Datum '31.12.2089'
        tbaD AT tmpD : ARRAY[-1..10] OF BYTE ;

    tmpT : STRING[8] ;  // String für Uhrzeit '23:59:59'
        tbaT AT tmpT : ARRAY[-1..8] OF BYTE ;
END_VAR

    // IN-DATE_AND_TIME in TEMP kopieren
    tmpDT := IN;

    // Datum --> '31.12.2089'
    tmpD := '00.00.2000'; // String initialisieren

    // Tag
    tbaD[1] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Day), N:=4) ) OR B#16#30;
    tbaD[2] := baDT.Day AND B#16#0F OR B#16#30;

    // Monat 
    tbaD[4] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Month), N:=4) ) OR B#16#30;
    tbaD[5] := baDT.Month AND B#16#0F OR B#16#30;
    
    // Jahr
    tbaD[9] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Year), N:=4) ) OR B#16#30;
    tbaD[10] := baDT.Year AND B#16#0F OR B#16#30;

    IF tbaD[9] = CHAR_TO_BYTE('9') THEN
       tbaD[7] := CHAR_TO_BYTE('1');      // 19 für 1990..1999
       tbaD[8] := CHAR_TO_BYTE('9');
    END_IF;

    // Uhrzeit --> '23:59:59'
    tmpT := '00:00:00'; // String initialisieren

    // Stunde
    tbaT[1] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Hour), N:=4) ) OR B#16#30;
    tbaT[2] := baDT.Hour AND B#16#0F OR B#16#30;

    // Minute
    tbaT[4] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Minute), N:=4) ) OR B#16#30;
    tbaT[5] := baDT.Minute AND B#16#0F OR B#16#30;

    // Sekunde
    tbaT[7] := DWORD_TO_BYTE( SHR(IN:=BYTE_TO_DWORD(baDT.Second), N:=4) ) OR B#16#30;
    tbaT[8] := baDT.Second AND B#16#0F OR B#16#30;
 
    // Datum-String und Uhrzeit-String nach OUTPUT kopieren
    baDout := tbaD;
    baTout := tbaT;

END_FUNCTION

Harald
 
In SCL sollte es nicht nötig sein, explizit BLKMOV aufzurufen. Das kann man dem Compiler überlassen, ob er BLKMOV einsetzt oder die paar Bytes gleich direkt kopiert. Schon alleine das Zusammenbasteln der 2 ANY für BLKMOV macht mindestens soviel Aufwand wie die 12 + 10 Bytes direkt mit L/T zu kopieren. Und da hat BLKMOV noch nichtmal angefangen ... Und falls man den Code mal zu TIA migrieren muß bekommt man zusätzliche Probleme wenn man explizit BLKMOV verwendet hat.

Das sehe ich auch genau so!

Was aber wenig bekannt ist und nur schwammig in der Siemens Doku steht, dass BLKMOV automatisch korrekt die Zeichen zwischen ByteArray und String hin und her kopieren kann.

Was ich aber auch erst durch deinen Code gelernt hab ist:
- dass man Views auf Variablen auch für Ausgänge erstellen kann. Ich war irgendwie fest davon überzeugt, dass geht nur bei Temp.
Da hab ich schon Code mit individuell erstellen ANYs gebastelt! Es wäre so einfach gewesen!
- eine negativer Index für Arrays! Genial! Darauf wär ich nie gekommen!
 
Zurück
Oben