Sonstiges SCL: Problem beim Vergleich von zwei Datenwerten

Beogradjanin

Level-1
Beiträge
21
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Tag,

ich versuche zur Zeit einen Baustein in SCL zu programmieren, den ich in Verbindung mit dem CountOh nutzen will.
Der CountOh berechnet die Betriebsstunden für das aktuelle Jahr. Ich benötige aber diesen Wert auch für das letzte Jahr...

Beispiel:
2013: aktuell 200h (Ende des Jahres 480h)
2012: 500h

---
2014: aktuell 100h
2013: 480h

etc.

Dieser Wert von dem aktuellen Jahr (OhActualYear) soll an jedem neuen Jahr am 1.1. um 00:00 Uhr in ein Variable Namens OhLastYear gespeichert werden.

Ich habe bis jetzt so einiges programmiert, nur irgendwie klappt der Vergleich nicht, das, wenn die aktuelle Zeit größer als die Vergleichszeit ist, dass er den Wert von OhActualYear nach OhLastYear schreibt und den OhActualYear auf 0 setzt.

Code:
FUNCTION_BLOCK FB1111
TITLE ='Speichert die Betriebsstunden aus dem letzten Jahr'
NAME:'SaveOpH'
AUTHOR:'Lukic'
FAMILY:'TIME'
VERSION: '1.0'




// Typical-Attribute
{
  S7_m_c:='true';
  S7_blockview:='big'
}


//Input: From Block CountOh
VAR_INPUT  
    Days:       INT;
    Hours:      INT;
    Minutes:    INT; 
    ActDateTime: DATE_AND_TIME;
END_VAR


//Output
VAR_OUTPUT
    //Operating hours of the actual year
    OhActualYear: REAL;    
    //Operating hours of the last year
    OhLastYear:   REAL;    
    //Date to save the operating hours to OhLastYear and reset OhActualYear
    CmpDate: DATE_AND_TIME;
    gr : BOOL;
END_VAR


VAR
    CmpDateTime: DATE_AND_TIME;
    arrayCmpDateTime AT CmpDateTime:
       STRUCT
        Jahr: Byte;
        Monat: BYTE;
        Tag: BYTE;
        Stunde: BYTE;
        Minute: BYTE;
        Sekunde: BYTE;
        MS_UND_WDAY: WORD;
    END_STRUCT;  
    
    DateTime: DATE_AND_TIME;
    arrayDateTime AT DateTime:
    STRUCT
        Jahr: Byte;
        Monat: BYTE;
        Tag: BYTE;
        Stunde: BYTE;
        Minute: BYTE;
        Sekunde: BYTE;
        MS_UND_WDAY: WORD;
   END_STRUCT;  
    
       thisYear:INT;
       tYear:INT;  
END_VAR


BEGIN


tYear:=READ_CLK(CDT :=DateTime); 


//Working Hours of the actual year
OhActualYear:=(Days*24)+Hours+(INT_TO_REAL(Minutes)/60);


thisYear:=BCD_TO_INT(arrayDateTime.Jahr);
//next Year
arrayCmpDateTime.Jahr := INT_TO_BYTE(thisYear+1);
CmpDate:=CmpDateTime;


gr:=GE_DT(DT1 := actDateTime,DT2 := CmpDate); // BOOL


;
    
END_FUNCTION_BLOCK

Der Code ist noch nicht vollständig. Bis jetzt will ich nur anhand einer Hilfsvariablen namens "gr" testen, ob der Vergleich funktioniert.
Und das tut er nicht.

Also mache ich irgendwas falsch.

Oder kann man das ganze Problem deutlich einfacher verwirklichen?
 
Hallo,
was funktioniert denn nicht so, wie gewünscht ?
Ich kenne jetzt den von dir verwendeten Baustein (GE_DT) nicht vom eigenen benutzen - für mich steht aber das GE in der Namens-Bezeichnung für "Greater or Equal" also "größer oder gleich".
Je nachdem, wierum die beiden Operanden (DT1 und DT2) verglichen werden würdest du m.E. immer ein "TRUE" oder immer ein "FALSE" erhalten.

Wegen vereinfachen : es läßt sich alles immer anders machen. Dein Ansatz ist aber so doch erstmal nicht verkehrt ...

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

mein Problem ist, dass beide Zeitwerte (aktuelle Zeit und Vergleichszeit) korrekt dargestellt werden, der Baustein GE_DT (du hast richtig vermutet, es ist ein greater or equal-Baustein) bei den zwei Zeitwerten aber nicht den korrekten Bool'schen Wert wiedergibt.

Ich hatte die aktuelle Zeit mal Testweise auch an einen Ausgangspin übergeben, so dass ich mit dem Eingang "ActDateTime" irgendeinen Wert am Input zuweisen konnte, der an den Ausgang "OutActDateTime" übergeben wurde.

Diesen "OutActDateTime" Ausgang und den "CmpDate" Ausgang hatte ich sogar testweise an den GE_DT händisch verschaltet. An den DT1 habe ich den OutActDateTime und an den DT2 den CmpDate-Pin verschaltet.

Nun hatte ich folgende Werte getestet:

OutActDateTime: 12-06-25-00:00:00 (so in etwa war die Schreibweise; stellt das Datum 25.06.2012 dar)
CmpDate: 13-01-01-00:00:00 (-> 01.01.2013)

Am GE_DT-Ausgang wurde (korrekt) eine 0 ausgegeben.

Nun habe ich den OutActDateTime-Wert auf 14-06-25-00:00:00 (-> 25.06.2014) verändert und den CmpDate-Wert bei 01.01.2013 belassen.

Der Ausgang von GE_DT hat sich nicht auf 1 geändert.


Also stimmt irgendwas nicht mit der Übergabe oder der Variablendeklaration in meinem Quellcode.
 
Bei mir sieht die AT-Ansicht so aus:


Code:
   datDate: DATE_AND_TIME;
   atdatDate AT datDate : STRUCT
                             Jahr: BYTE;
                             Monat: BYTE;
                             Tag: BYTE;
                             Stunde: BYTE;
                             Minute: BYTE;
                             Sekunde: BYTE;
                             MSBLSB: BYTE;
                          END_STRUCT;

MSBLSB ist ein Byte, nicht ein Word, wie in deinem Code. Vielleicht liegt es daran?
 
@Ralle:
Das ist zwar nicht schön, was der TE da mit dem Ding gemacht hat - stimmt aber. Bei dir fehlt noch ein Byte (das Millisekunden Teil 3 + Wochentag) in der Struct - Millisekunden sind 1,5 Byte und der Wochentag 0,5 Byte - also insgesamt ein WORD.

Gruß
Larry
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@TE:
was mir noch so aufgefallen ist :
wo wird das CmpDatTime ("CmpDate:=CmpDateTime;") sinnvoll aufgefüllt ?
Du beschreibst zwar das Jahr davon in der AT-Sicht, den Rest aber nicht und ggf. ist der restliche Inhalt nicht sinnvoll ...

Gruß
Larry
 
@Larry
Du hast Recht, bei mir fehlt das letzte Byte mit LSB der Sekunden und dem Tag.
Aber die AT-Ansicht funktioniert in meinem Programm trotzdem.
Gut zu wissen, aber dann ist das leider nicht der Fehler im Programm des TE! :confused:
 
Ich schätze, bei Deinen Tests hast Du irgendeine DT-Variable manipuliert, aber eine andere DT-Variable mit CmpDate verglichen. Oder OutActDateTime ein unzulässig formatiertes DT zugewiesen ...

Unabhängig davon würde ich aber ein anderes Vorgehen vorschlagen. Falls die SPS-Uhr irgendwie synchronisiert oder verstellt wird und/oder die SPS zu Neujahr 0:00 Uhr gar nicht eingeschaltet ist, kann es zu fehlerhafter/mehrfacher Ausführung Deiner Datenübernahme als Vorjahresdaten kommen.

Ich würde mir nur merken, wann das letzte Mal die Betriebsstunden als Vorjahresdaten umkopiert wurden - es reicht, sich die Jahreszahl zu merken. Unterscheidet sich die Jahreszahl (BYTE) des aktuellen Datums von der gemerkten Jahreszahl, dann die Betriebsstunden kopieren und die Jahreszahl des aktuellen Datums in den Jahresmerker kopieren.

Etwa so:
Code:
IF ActualDate.Jahr <> lastSavedYear AND ActualDate.Monat = 1 THEN
  lastSavedYear := ActualDate.Jahr ;
  OhLastYear := OhActualYear ;
  OhActualYear := 0.0 ;
END_IF ;

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@TE:
was mir noch so aufgefallen ist :
wo wird das CmpDatTime ("CmpDate:=CmpDateTime;") sinnvoll aufgefüllt ?
Du beschreibst zwar das Jahr davon in der AT-Sicht, den Rest aber nicht und ggf. ist der restliche Inhalt nicht sinnvoll ...

Gruß
Larry

Hallo,

in wiefern sinnvoll? Verstehe es nicht so ganz. Ich habe das Jahr des Arrays "arrayCmpDateTime" einfach um 1 erhöht, so dass das nächste Jahr reingeschrieben wird.

Jetzt stellt sich mir nur die Frage, weil ich bei dem ganzen vorhaben in das Array nur das Jahr beschreibe, wie es mit den Monaten und Tagen ausschaut. Im CFC-Editor (arbeite ja mit PCS7) zeigt mir der Ausgang bei der Simulation z.B. den 01.01.2014 an. So will ich es auch. Nur könnte es evtl. sein, dass der 1.1 als Standardwert angezeigt wird, wenn der Inhalt nicht in Ordnung ist?

@PN/DP:
Der Ansatz ist nicht schlecht. Nur irgendwie müsste es auch mit dem Baustein GE_DT funtionieren. Zumindest funkioniert es, wenn ich die Zeiten und den Baustein mittels PCS7 simuliere. Also nicht meinen selbstgeschriebenen Baustein nehme.

@Ralle:
Danke für die Mühe. Den Aufbau des Structs hatte ich irgendwo (ich glaube sogar hier im Forum) gefunden.
 
@TE:
Ich denke, dass die DATE-Darstellung das automatisch macht, da sie sich auf den Tag bezieht (Anzahl der Tage, die seit dem 01.01.1990 vergangen sind). In deinem Fall wäre aber die Datum-Zuweisung (und darauf wollte ich dich hinweisen) ggf. der 00.00.2014 - und das kann ggf. nicht verglichen werden.
Deshalb hatte ich die auf die Zuweisung des CmpDateTime hingewiesen. Vielleicht checkst du das noch einmal gegen.

Gruß
Larry
 
Ich habe es vorhin sogar probiert, den einzelnen Bytes einen expliziten Wert zuzuweisen.

Beispiel:

arrayCmpDateTime.Jahr := INT_TO_BYTE(14);
arrayCmpDateTime.Monat := INT_TO_BYTE(2);
arrayCmpDateTime.Tag := INT_TO_BYTE(5);
arrayCmpDateTime.Stunde := INT_TO_BYTE(0);
arrayCmpDateTime.Minute := INT_TO_BYTE(0);
arrayCmpDateTime.Sekunde := INT_TO_BYTE(0);
arrayCmpDateTime.MS_UND_WDAY := INT_TO_WORD(1);

Klappt aber trotzdem nicht :confused:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
... jetzt müßte man dann wissen, wie der GE_DT genau funktioniert.
Es gibt nämlich auch noch die Möglichkeit, dass dein Datum in sich zwar OK ist, der angegebene Wochentag aber dazu nicht passt. Damit hatte ich in Verbindung mit Bereichszeigern bei Flex schon mal Probleme bekommen.
Ansonsten habe ich dazu keine weiteren Ideen mehr (außer, dass ich genau wie PN/DP, das auch anders lösen würde).

Gruß
Larry
 
Hab grad mal den GE_DT angeschaut. Ein falscher Wochentag stört nicht.
Es werden die einzelnen Bytes miteinander verglichen. Da die Millisekunden in der höherwertigen Stelle des Bytes sitzen, hat ein falscher Wochentag keinen Einfluss auf das Ergebnis (außer die Zeiten sind bis auf die Millisekunde identisch). Der Wochentag wird auch nicht separat überprüft.
 
In dem Code aus deinem Eingangspost ist auf jeden Fall ein Fehler.
Das Jahr wird BCD codiert gespeichert. Beim Lesen machst du es richtig, aber beim Schreiben weist du den Wert ohne Wandlung der Variable zu.
Du musst das Jahr darum nach der Berechnung wieder in BCD wandeln.

Probier mal
Code:
thisYear := BCD_TO_INT(arrayDateTime.Jahr);
//next Year
arrayCmpDateTime.Jahr := WORD_TO_BYTE(INT_TO_BCD(thisYear+1));

Funktioniert dank WORD_TO_BYTE sogar im Jahre 1999.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
In dem Code aus deinem Eingangspost ist auf jeden Fall ein Fehler.
Das Jahr wird BCD codiert gespeichert. Beim Lesen machst du es richtig, aber beim Schreiben weist du den Wert ohne Wandlung der Variable zu.
Du musst das Jahr darum nach der Berechnung wieder in BCD wandeln.

Probier mal
Code:
thisYear := BCD_TO_INT(arrayDateTime.Jahr);
//next Year
arrayCmpDateTime.Jahr := WORD_TO_BYTE(INT_TO_BCD(thisYear+1));

Funktioniert dank WORD_TO_BYTE sogar im Jahre 1999.

Hallo,

ich habe den Code von Thomas gerade getestet. Leider funktioniert die Wandlung nicht. Was ich auch hier nicht so ganz verstehe, ist, warum das "INT_TO_BCD(thisYear+1)" nicht für eine Wertwandlung von INT nach BCD ausreicht?
 
Stoppppppppppppp!!! Wer lesen kann, ist klar im Vorteil!

Es klappt doch so, wie es Thomas gesagt hat. Mein Fehler war eben, dass ich bei dem Code
Code:
thisYear := BCD_TO_INT(arrayDateTime.Jahr);
//next Year
arrayCmpDateTime.Jahr := WORD_TO_BYTE(INT_TO_BCD(thisYear+1));

In der letzten Zeile "arrayCmpDateTime.Jahr := WORD_TO_BYTE(INT_TO_BCD(thisYear+1));" noch INT_TO_BYTE stehen hatte. Glücklicherweise ist es mir eben noch aufgefallen und jetzt klappt es auch mit der Funktion GE_DT etc.

Juhuuuuuuuuu :p

Vielen lieben Dank an alle Beteiligte!
 
Zurück
Oben