TIA S7-300 Nummer des Tages im Jahr nach Datum berechnen

SPS-Andy

Level-2
Beiträge
58
Reaktionspunkte
9
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Forumsgemeinde,

ich stehe vor einem Problem. Ich erhalte ein Barcode in dem der Produktions Tag und Jahr angegeben ist. z.B. 243/22 Bedeutet Tag 243, Jahr 2022 = 31.08.2022.
Meine Aufgabe ist es nun aus dem 243 ein Datum zu ermitteln. Hier stehe ich auf dem Schlauch habe keinerlei Formel gefunden und/ oder mir herleiten können die mir das ausgibt.
Wie ich mir berechne ob es ein Schaltjahr ist (muss auch beachtet werden) weiß ich.
Hat oder kennt jemand eine Formel hier für?

Vielen Dank für die Hilfe

Zur Vollständigkeit:
ist eine ältere Anlage S7-314c/2PN
und TIA V13
 
Servus,

ich würd es mal so angehen:
Eine DT-Variable auf den 1.1. des gewünschten Jahres setzen. (Wie hier die das gewünschte Jahr gesetzt werden kann müsste ich mir mal in Ruhe noch anschauen)
Dann addiere mit T_ADD n-1 Tage über eine Schleife dazu und deine DT sollte das gewünschte Datum haben.

Ist jetzt keine komplette Lösung, aber so würde ich anfangen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Evtl so:

Bsp:
Januar 31
Feb 59 (kein Schaltjahr)
März 90

Tage aus Barcode :58
Tage aus Barcode(58) < Tage Feb (59) = Februar

Tage aus Barcode (58) - Anzahl der Vergangenen Tage bis Monats Anfang (Januar 31) +1 = 28

Also ist das Datum der 28.02.2022

Die Tage im Monat als Array mit Defaultwerten anlegen, dann lässt sich das ganze zackig in ner Schleife berechnen und über den Index kann man den Monat auslesen

1661957194233.png
 
Hallo.
Zwei Möglichkeiten:
1. Monat verwenden um vollständige Monate als Summe zu berechnen. Dann noch Tag aktueller Monat...
Also z. B. Monat = 5 (31+28+31+30) Januar - April
Dann noch Tagesdatum...fertig
2. Berechnung über SCL bzw. mit TimDiff. Zwischen aktuellem DateTime und dem 01.01. Des Jahres...
Ich find Variante 1 leichter umzusetzen.
Gruß
 
Zu der 22 einmal 2000 addieren, also die Jahreszahl 2022 bilden und dann noch Tag 1 und Monat 1 hinzufügen (also 2022-01-01).
Dies in die serielle Darstellung wandeln (lassen). Dann n-1 (hier 242) dazu addieren und wieder ins DatumsFormat umwandeln.
Sorry, dass ich mich so verschleiert ausdrücke, aber ich habe gerade keinen Nerv, mich in DatenTypKonvertierungen hineinzuvertiefen.
Das Datum ist doch intern nur eine laufende Zählung der Tage und die Konvertierungen von dieser internen Darstellung in Tag-Monat-Jahr und zurück gibt es doch(?). Die DifferenzBildung wurde schon genannt und die hier benötigte Addition sollte genauso problemlos funktionieren.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ohne es geprüft zu haben, aber ist der Datentyp "Date" nicht eigentlich ein DINT mit der Anzahl Tagen seit dem 01.01.1970?
Also könnte es reichen einfach die Tage bis zum 01.01.2022 + die Anzahl Tage im Jahr zu summieren und in "Date" zu wandeln/kopieren (in AWL)?
 
Das hier sollte funktionieren.
Es hat zwar noch keine Abfragen ob der String-Input das richtige Format hat, aber die Funktion an und für sich sollte gegeben sein.

Code:
FUNCTION "CalDay_To_Date" : Void
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
   VAR_INPUT
      CalDay : String;
   END_VAR

   VAR_OUTPUT
      "Date" : Date;
      DateTime : Date_And_Time;
   END_VAR

   VAR_TEMP
      tmp_dayString : String[3];
      tmp_yearString : String[2];
      tmp_dayInt : Int;
      tmp_yearInt : Int;
      tmp_pointer : Int;
      tmp_retval : Int;
      tmp_dateTime : Date_And_Time;
      tmp_dateTimeStruct : Struct
         year : Byte;
         month : Byte;
         day : Byte;
         hour : Byte;
         minute : Byte;
         second : Byte;
         milliSecon : Word;
      END_STRUCT;
      tmp_date : Date;
      i : Int;
   END_VAR


BEGIN
    //parse input
    #tmp_pointer:=FIND(IN1:=#CalDay, IN2:='/');
    #tmp_dayString := LEFT(IN := #CalDay, L := #tmp_pointer - 1);
    #tmp_yearString := RIGHT(IN := #CalDay, L := 2);
    //convert input to int
    #tmp_dayInt := STRING_TO_INT(#tmp_dayString);
    #tmp_yearInt := STRING_TO_INT(#tmp_yearString);
    //set up Date_And_Time to the first day of specified year
    #tmp_dateTimeStruct.year := INT_TO_BCD16(#tmp_yearInt);;
    #tmp_dateTimeStruct.month := 1;
    #tmp_dateTimeStruct.day := 1;
    #tmp_retval:=BLKMOV(SRCBLK:=#tmp_dateTimeStruct, DSTBLK=>#tmp_dateTime);
    //add n - 1 days to set up Date_And_Time
    IF #tmp_dayInt > 1 THEN
        FOR #i := 1 TO #tmp_dayInt - 1 DO
            #tmp_dateTime := T_ADD(IN1 := #tmp_dateTime, IN2 := t#1d);
        END_FOR;
    END_IF;
    // write outputs
    #DateTime := #tmp_dateTime;
    #Date := DT_TO_DATE(#tmp_dateTime);
END_FUNCTION

Ausgabe einmal für DT und einmal nur für Date.

Wenn man DT nicht benötigt, kann man, wie Holzmichl in #9 vorgeschlagen hat und auch effizienter wäre, die DT auch gleich auf Date, dann auf DInt konvertieren und einfach n-1 addieren.
Anschließend wieder auf Date zurück konvertieren.

Date hätte zwar nur 2 Byte, was eher einem Int gleichen würde, aber so wie es aussieht kann man nur von DInt nach Date konvertieren.
 
Zuletzt bearbeitet:
Ohne es geprüft zu haben, aber ist der Datentyp "Date" nicht eigentlich ein DINT mit der Anzahl Tagen seit dem 01.01.1970?
Also könnte es reichen einfach die Tage bis zum 01.01.2022 + die Anzahl Tage im Jahr zu summieren und in "Date" zu wandeln/kopieren (in AWL)?
Nicht ganz. Es ist ein 16 Bit Word (UINT) 0 = D#1990-1-1 bis 65378 (W#16#FF62) = D#2168-12-31

Das Datum des x-ten Tages eines Jahres läßt sich tatsächlich einfach durch D#jjjj-01-01 - 1 + x berechnen, allerdings müsste man dafür erst noch die Tagnummer D#jjjj-01-01 (das "DateSerial" des 1. Januar des Jahres) berechnen oder als vorberechnete Tabelle hinterlegen.

Meine Aufgabe ist es nun aus dem 243 ein Datum zu ermitteln.
Und was sollst Du mit dem Datum machen? Nur auf einem HMI anzeigen? Da gibt es vielleicht schon DateSerial- und DateAdd-Funktionen?

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Für welchen Datumsbereich muß die Umrechnung funktionieren?

Man könnte
- eine DATE_AND_TIME-Variable nehmen und mit DT#1990-01-01-00:00:00.000 initialisieren
- das Jahr jjjj (1990 bis 2089) in das erste Byte hineinbasteln ( BCD#0 = 2000 ... BCD#89 = 2089, BCD#90 = 1990 ... BCD#99 = 1999 )
- mit T_CONV in das DateSerial D#jjjj-01-01 wandeln
- die Tagnummer x - 1 addieren --> ergibt das DATE D# (DateSerial) für das gegebene Jahr + Tag x

Harald
 
(auch ungetestet, Funktion in #12 beschrieben)
Code:
FUNCTION "DayYearStr_to_Date" : Date
{ S7_Optimized_Access := 'FALSE' }
AUTHOR : PN_DP
VERSION : 0.1
   VAR_INPUT
      DayYearStr : String;   // Tag/Jahr-String in der Art 'ddd/yy'
   END_VAR

   VAR_TEMP
      tmp_dayString : String[3];
      tmp_yearString : String[2];
      tmp_dayInt : Int;
      tmp_yearInt : Int;
      tmp_pointer : Int;
      tmp_DT : Date_And_Time;
      tmp_DTstruct AT tmp_DT : Struct
         YEAR   : Byte;
         MONTH  : Byte;
         DAY    : Byte;
         HOUR   : Byte;
         MINUTE : Byte;
         SECOND : Byte;
         MSEC12 : Byte;
         MSEC3WEEKDAY : Byte;
      END_STRUCT;
      tmp_DateSerial : DInt;
   END_VAR

BEGIN
//DATE aus Tag/Jahr-String in der Art 'ddd/yy' berechnen
//die Nummer des Tages ist max dreistellig: '1'..'366'
//das Jahr ist immer zweistellig: '00'..'89', '90'..'99' entsprechend 2000..2089 und 1990..1999

//parse input
#tmp_pointer := FIND(IN1 := #DayYearStr, IN2 := '/');
#tmp_dayString := LEFT(IN := #DayYearStr, L := #tmp_pointer - 1);
#tmp_yearString := RIGHT(IN := #DayYearStr, L := 2);
//convert input to int
#tmp_dayInt := STRING_TO_INT(#tmp_dayString); //1..366
#tmp_yearInt := STRING_TO_INT(#tmp_yearString); //0..99

//add n - 1 days to year
#tmp_DT := DT#1990-01-01-00:00:00.000; //DATE_AND_TIME-Variable initialisieren
#tmp_DTstruct.YEAR := WORD_TO_BYTE(INT_TO_BCD16(#tmp_yearInt));
#tmp_DateSerial := DATE_TO_DINT(DT_TO_DATE(#tmp_DT)) + #tmp_dayInt - 1;

//write return value
#DayYearStr_to_Date := DINT_TO_DATE(#tmp_DateSerial);
END_FUNCTION

Harald
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
hier eine umfachreiche DateInfo Funktion.
liefert folgende Daten zu einem S7-Date

iYYYY : INT; // Year : [1990..2168]
iMM : INT; // Month : [1..12]
iDD : INT; // Day : [1..31]
WeekDayISO : INT; // Weekday ISO 8601 [1..7] = [Mo..Su]
WeekDayNA : INT; // Weekday Nothern Amerika [1..7] = [Su..Sa]
iWeek_ISO : INT; // Week ISO 8601, [1..53], EU, start on Monday
iWeek_NA : INT; // Week Northern America, [1..54], start on Sunday
iDayOfYear : INT; // Day No in the year [1..366]
iDaysInMonth : INT; // No of days in month [28..31]
xLeapYear : BOOL; // Year is leap year / Schaltjahr
xIso53WeekYear: BOOL; // Year contains 53 weeks according to ISO 8601

Da der Code das Limit von 1000 Zeichen überschreitet, kann ich ihn nicht direkt posten, sondern
muss ihn als Anhang anfügen. Geschrieben ist der Code in SCL unter Step7 Classic V5.5
 

Anhänge

  • S7_DateInfo.SCL.txt
    24,3 KB · Aufrufe: 10
hier eine umfachreiche DateInfo Funktion.
liefert folgende Daten zu einem S7-Date

iYYYY : INT; // Year : [1990..2168]
iMM : INT; // Month : [1..12]
iDD : INT; // Day : [1..31]
WeekDayISO : INT; // Weekday ISO 8601 [1..7] = [Mo..Su]
WeekDayNA : INT; // Weekday Nothern Amerika [1..7] = [Su..Sa]
iWeek_ISO : INT; // Week ISO 8601, [1..53], EU, start on Monday
iWeek_NA : INT; // Week Northern America, [1..54], start on Sunday
iDayOfYear : INT; // Day No in the year [1..366]
iDaysInMonth : INT; // No of days in month [28..31]
xLeapYear : BOOL; // Year is leap year / Schaltjahr
xIso53WeekYear: BOOL; // Year contains 53 weeks according to ISO 8601

Da der Code das Limit von 1000 Zeichen überschreitet, kann ich ihn nicht direkt posten, sondern
muss ihn als Anhang anfügen. Geschrieben ist der Code in SCL unter Step7 Classic V5.5
:unsure:
Hilft dem TE aber leider nicht wirklich weiter, oder?

Er möchte in der umgekehrten Richtung aus iYYYY und iDayOfYear ein out_S7_DATE berechnen!
(Ja, die Thread-Überschrift suggeriert auch mir etwas anderes als der Eröffnungspost. 🤷‍♂️)
 
Das gibt's auch "CreateDate"

Code:
FUNCTION "m7_CreateDate" : DATE
    TITLE = 'Create DATE-Format from Year/Month/Day'

// ==============================================================================
//
// Erezugt das Date-Format aus den Angaben Year/Month/Day
//
// Die Berechung macht sich die Integerrundung zu nutze, um die Schalttage korrekt
// zu berechnen. Alle Divisionen müssen Integerdivisionen sein (immer abgerundet).
// Berechnet wird die Anzahl der Tage nach dem Gregorinischen Kalender seit
// Christi Geburt.
//
// Bedingung für ein Schaltjahr (mit 29.Feb)
// - ein Schaltjahr ist alle 4 Jahre (Jahreszahl ist durch 4 teilbar)
// - das Schaltjahr fällt alle volle 100 Jahre aus
// - das Schaltjahr fällt alle volle 400 Jahre nicht aus (findet also statt)
//
// function CrateDate(y,m,d)  // Calculates the Daynumber from Date
//
// Die Berechnung erfolgt nach folgender Formel
// zuerst werden die Monats-Nummern verschoben:
// 0=März bis 11=Februar (wegen Schalttagberechnung)
//
// m = (m + 9) Mod 12   // verschiebt die Monate 0=März bis 11=Februar
//
// Wenn Monat = Januar oder Februar, dann 1 von Jahren abziehen, dies wird
// wegen der Verschiebung der Monate nötig. Für die Schaltjahresberechung
// ist das ebenfalls notwendig, da im Falle eines Schaltjahres der Februar
// noch nicht vorbei ist und ein evtl. Schalttag (29.Feb.) noch nicht
// eingefügt sein kann. Die fehlenden Tage werden bei der Berechnung der Montastage
// mit (m*306 +5)/10 wieder korrekt draufgerechnet. Zum Schluss muss 1 Tag abgezogen
// werden, da Christi Geburt Tag 0 ist!
//
// mit dem Trick (m*306+5)/10 erreicht man durch die Integerrundung, dass alle Monate mit der
// korrekten Tageszahl 30 oder 31 berechnet werden (selbst die doppelte 31 von Juli, August wird
// korrekt abgebildet. Der Februar als einziger Monat mit weniger Tagen steht am Schluss (Nr.11) und
// ist somit für die 30/31 Tage Berechnung irrelevant, da die Berechnung nur die Tage der
// vollständig vergangenen Vormonate abbilden muss. Die Tage des akt. Monats werden über +d addiert.
//
// y = y - m/10   // m/10 =1 für Monate Jan. & Feb. (Monats-Nr. 10 & 11)
//
// return (365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + d - 1 )
// ==============================================================================
//
// AUTHOR: S.Maag
// DATE: 10/2008

// CHANGELOG:
// ------------------------------------------------------------------------------
// DATE         NAME        DESCRIPTION
// ------------------------------------------------------------------------------
// 26.02.2020   S.Maag      getestet und Fehler "-dd" behoben, muss "+dd" sein
// 31.07.2017   S.Maag      Coverted AWL TO SCL version
// ------------------------------------------------------------------------------
 
    VERSION : '3.0'
    AUTHOR  : 'S.Maag'
    FAMILY  : 'Maagic7'


VAR_INPUT
  iYear : INT ;    // Year
  iMonth : INT ;   // Month
  iDay : INT ;     // Day
END_VAR

VAR_TEMP
  yy : DINT ;   
  mm : DINT ;   
  dd : DINT ;   
END_VAR

BEGIN
    mm := (INT_TO_DINT(iMonth) + 9) MOD 12;  // Jan..Dez/1..12 => März..Feb/0..11
    yy := INT_TO_DINT(iYear) - mm/10;   // -1, wenn Monat = Jan. oder Feb. Anzahl Tage >306 (SummeTage[März..Dez]=306)
    dd := INT_TO_DINT(iDay);

    // L#726773 = "Anzahl Tage bis 31.12.1989" + 306 = 726467 + 306; in Step 7 ist 0 = 01.01.1990
    // Dass hier nochmals eine Korrektur von 306 Tagen drin ist liegt an der Verschiebung der Monate 0..11/März..Feb
    // Klar ist mir das nicht, da die Korrektur bereits über YY-mm/10 erfolgt sein sollte! Aber es funktioniert korrekt!
    m7_CreateDate := DINT_TO_DATE(365*yy + yy/4 - yy/100 + yy/400 +(mm*306 +5)/10 + dd -(1+726773));
END_FUNCTION
 
Hallo,

danke für die Anregungen. Ich habe mich für folgende Lösung entschieden, hatte noch Hilfe von einem befreundetem Programmierer. Danke!

Code:
//Jahr von Zeichen zu Int wandeln

IF #in_Jahr_X0 = ' ' AND #in_Jahr_0X = ' ' THEN
    #Jahr_int := 00;
ELSE
    #Jahr_int := 10 * (BYTE_TO_INT(#in_Jahr_X0) - 48) + (BYTE_TO_INT(#in_Jahr_0X) - 48);
END_IF;

IF (#Jahr_int MOD 4) = 0 THEN
    #Schaltjahr := TRUE;
ELSE
    #Schaltjahr := FALSE;
END_IF;

// Tag des Jahres von Zeichen zu Integer wandeln
IF #in_TdJ_X00 = ' ' AND #in_TdJ_0X0 = ' ' AND #in_TdJ_00X = ' ' THEN
    #TdJ_int := 0;
ELSE
    #TdJ_int := 100 * (BYTE_TO_INT(#in_TdJ_X00) - 48) + 10 * (BYTE_TO_INT(#in_TdJ_0X0) - 48) + (BYTE_TO_INT(#in_TdJ_00X) - 48);
END_IF;
IF NOT #Schaltjahr THEN
    CASE #TdJ_int OF
        1..31:  // Januar
            #out_Monat_X0 := '0';
            #out_Monat_0X := '1';
            #Tag_int := #TdJ_int;
        32..59:  // Februar
            #out_Monat_X0 := '0';
            #out_Monat_0X := '2';
            #Tag_int := #TdJ_int - 31;
        60..90:  // März
            #out_Monat_X0 := '0';
            #out_Monat_0X := '3';
            #Tag_int := #TdJ_int - 59;
        91..120:  // April
            #out_Monat_X0 := '0';
            #out_Monat_0X := '4';
            #Tag_int := #TdJ_int - 90;
        121..151:  // Mai
            #out_Monat_X0 := '0';
            #out_Monat_0X := '5';
            #Tag_int := #TdJ_int - 120;
        152..181:  // Juni
            #out_Monat_X0 := '0';
            #out_Monat_0X := '6';
            #Tag_int := #TdJ_int - 151;
        182..212:  // Juli
            #out_Monat_X0 := '0';
            #out_Monat_0X := '7';
            #Tag_int := #TdJ_int - 181;
        213..243:  // August
            #out_Monat_X0 := '0';
            #out_Monat_0X := '8';
            #Tag_int := #TdJ_int - 212;
        244..273:  // September
            #out_Monat_X0 := '0';
            #out_Monat_0X := '9';
            #Tag_int := #TdJ_int - 243;
        274..304:  // Oktober
            #out_Monat_X0 := '1';
            #out_Monat_0X := '0';
            #Tag_int := #TdJ_int - 273;
        305..334:  // November
            #out_Monat_X0 := '1';
            #out_Monat_0X := '1';
            #Tag_int := #TdJ_int - 304;
        335..365:  // Dezember
            #out_Monat_X0 := '1';
            #out_Monat_0X := '2';
            #Tag_int := #TdJ_int - 334;
        ELSE
            #out_Monat_X0 := ' ';
            #out_Monat_0X := ' ';
            #Tag_int := 0;
    END_CASE;
ELSE
    CASE #TdJ_int OF
        1..31:  // Januar
            #out_Monat_X0 := '0';
            #out_Monat_0X := '1';
            #Tag_int := #TdJ_int;
        32..60:  // Februar
            #out_Monat_X0 := '0';
            #out_Monat_0X := '2';
            #Tag_int := #TdJ_int - 31;
        61..91:  // März
            #out_Monat_X0 := '0';
            #out_Monat_0X := '3';
            #Tag_int := #TdJ_int - 60;
        92..121:  // April
            #out_Monat_X0 := '0';
            #out_Monat_0X := '4';
            #Tag_int := #TdJ_int - 91;
        122..152:  // Mai
            #out_Monat_X0 := '0';
            #out_Monat_0X := '5';
            #Tag_int := #TdJ_int - 121;
        153..182:  // Juni
            #out_Monat_X0 := '0';
            #out_Monat_0X := '6';
            #Tag_int := #TdJ_int - 152;
        183..213:  // Juli
            #out_Monat_X0 := '0';
            #out_Monat_0X := '7';
            #Tag_int := #TdJ_int - 182;
        213..244:  // August
            #out_Monat_X0 := '0';
            #out_Monat_0X := '8';
            #Tag_int := #TdJ_int - 212;
        245..274:  // September
            #out_Monat_X0 := '0';
            #out_Monat_0X := '9';
            #Tag_int := #TdJ_int - 244;
        275..305:  // Oktober
            #out_Monat_X0 := '1';
            #out_Monat_0X := '0';
            #Tag_int := #TdJ_int - 274;
        306..335:  // November
            #out_Monat_X0 := '1';
            #out_Monat_0X := '1';
            #Tag_int := #TdJ_int - 305;
        336..366:  // Dezember
            #out_Monat_X0 := '1';
            #out_Monat_0X := '2';
            #Tag_int := #TdJ_int - 335;
        ELSE
            #out_Monat_X0 := ' ';
            #out_Monat_0X := ' ';
            #Tag_int := 0;
    END_CASE;
END_IF;

// Tag von Int zu Zeichen wandeln
CASE #Tag_int OF
    1..9:
        #out_Tag_X0 := '0';
        #out_Tag_0X := INT_TO_BYTE(#Tag_int + 48);
    10..19:
        #out_Tag_X0 := '1';
        #out_Tag_0X := INT_TO_BYTE(#Tag_int - 10 + 48);
    20..29:
        #out_Tag_X0 := '2';
        #out_Tag_0X := INT_TO_BYTE(#Tag_int - 20 + 48);
    30..31:
        #out_Tag_X0 := '3';
        #out_Tag_0X := INT_TO_BYTE(#Tag_int - 30 + 48);
    ELSE
        #out_Tag_X0 := ' ';
        #out_Tag_0X := ' ';
END_CASE;
 
Zurück
Oben