TIA Jahreszähler

sps_mitte

Level-2
Beiträge
172
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Forum,
hat jemand ein Muster zum Jahreszähler bzw. Mengenzähler ?
Eine Variable relativ hochzählen und in jeweils Monate aufteilen.

Könnte mir vorstellen per Counter hochzuzählen und den Wert in ein DB zu sichern. Monat Januar
Dann den nächsten Counter hochzählen und den Wert wieder in ein DB sichern. Monat Februar
usw.
 
Eine Variable relativ hochzählen und in jeweils Monate aufteilen.

Wo liegt denn die Jahresproduktion? Also wie hoch würde theoretisch gezählt werden?
Ein einfacher DINT geht ja schon mal bis ca. 2.15 Milliarden:
2.147.483.647

Ein Word ( mit dem man ja theoretisch nicht rechnen kann ) bis > 4 Mrd.
4.294.967.295
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Unter Umständen kann man auch mit ULINT zählen. Das ginge dann bis zu AchtzehnTrillionVierhundertsechsundvierzigBilliardenSiebenhundertvierundvierzigBillionDreiundsiebzigMilliardenSiebenhundertneunMillionenFünfhunderteinundfünzigTausendSechshundertfünfzehn, falls ich es richtig in Erinnerung habe :ROFLMAO: .

In Ziffern ausgedrückt: 18_446_744_073_709_551_615
 
Unter Umständen kann man auch mit ULINT zählen. Das ginge dann bis zu AchtzehnTrillionVierhundertsechsundvierzigBilliardenSiebenhundertvierundvierzigBillionDreiundsiebzigMilliardenSiebenhundertneunMillionenFünfhunderteinundfünzigTausendSechshundertfünfzehn, falls ich es richtig in Erinnerung habe :ROFLMAO: .

In Ziffern ausgedrückt: 18_446_744_073_709_551_615

Da stellt sich die Frage nach der verwendeten Steuerung ( und wie hoch eigentlich gezählt werden soll/muss )
 
Code:
// TYPE auskommentieren, sonst wird bei jeder Übersetzung Zeitstempel neu generiert,
// was zu manuellem Aktualisierungszwang führen kann

(*
TYPE "m7_udtStatisticCNT_DINT"
  AUTHOR : 'S.Maag'
  FAMILY : Maagic7
  VERSION : 0.1

  STRUCT     
   all : DINT ;    //Counter: overall (Gesamtzähler)
   hour : DINT ;    //Counter: hour
   day : DINT ;    //Counter: day
   week : DINT ;    //Counter: week
   month : DINT ;    //Counter: month
   year : DINT ;    //Counter: year
   user : DINT ;    //Counter: user (Zähler Benutzer z.B. Schichtzähler)
   DT_USER_RESET : DATE_AND_TIME ;    //Reset time of user counter (Zeit Benutzerzähler gelöscht)
  END_STRUCT ;    
END_TYPE
*)


FUNCTION "m7_StatisticCounter_DINT" : VOID
    TITLE ='Statistic counter hour, day, week, month, year, user'
// ==============================================================================
//
// ==============================================================================
//
// BENÖTIGTE SFCs: SFC  1 - READ_CLK
//
// AUTHOR: S.Maag
// DATE: 7/2013
//
// CHANGELOG:
// ------------------------------------------------------------------------------
// DATE         NAME      DESCRIPTION
// ------------------------------------------------------------------------------
// 27.03.2020   S.Maag    AWL Version zu SCL, da SCL für diese Funktion 
//                        effektiver und einfacher ist.
//                        AWL 2844 BYTE; SCL 1554 BYTE
// 
// 04.09.2019   S.Maag    cfg_cnt_mms hinzugefügt, somit kann im hh:mm oder
//                        mm:ss Format die Zeit gezählt werden. Hierzu wird
//                        nach dem Zählervorgang eine Überlaufkorrektur der
//                        Werte vorgenommen, so dass in den letzten 2
//                        Stellen max ein Wert von 59 auftritt.
//
// 14.01.2016   S.Maag    Für Zähler Jahr war SysClockEvent Monat programmiert
//                        Reset war ebenfalls falsch old.year statt act.year
// --------------------------------------------------------------------------------

    VERSION : '3.0'
    AUTHOR  : 'S.Maag'
    FAMILY  : 'Maagic7'


VAR_INPUT
  COUNT : BOOL ;    //count: TRUE: counter=counter + step
  RESET_USER_COUNTER : BOOL ;    //Reset the user counter; (Benutzer Zähler löschen, z.B. Schichtwechsel)
  dSTEP : DINT ;    //Counter STEP; (Schrittweite Zähler)
  SysClkEvents : BYTE ;    //System Clock Events
  cfg_cnt_mmss : BOOL ;    //if TRUE it's counted in hh:mm or mm:ss Format
END_VAR

VAR_IN_OUT
  udt_cntDINT_act : "m7_udtStatisticCNT_DINT";    //actual counter data
  udt_cntDINT_old : "m7_udtStatisticCNT_DINT";    //old counter data
END_VAR

VAR_TEMP
  wSysCLKevt : WORD;
  SysCLK AT wSysCLKevt : STRUCT
      hiByte  : BYTE;  
      evtSEC  : BOOL ;    
      evtMIN  : BOOL ;    
      evtHOUR : BOOL ;    
      evtDAY  : BOOL ;    
      evtWEEK : BOOL ;    
      evtMONTH: BOOL ;    
      evtYEAR : BOOL ;    
      evtReserve : BOOL ; 
  END_STRUCT;
   
  SYS_CLK : DATE_AND_TIME ;    
  iRET : INT ;    
END_VAR


BEGIN
    
    wSysCLKevt := BYTE_TO_WORD(SysClkEvents);

    IF wSysCLKevt <> 0 THEN  // Anything to do? 0 means no acitve EVENT
    
        // RESET hour counter : (rücksetzen Zähler aktuelle Stunde)
        IF SysClk.evtHOUR THEN 
           udt_cntDINT_old.hour := udt_cntDINT_act.hour;
           udt_cntDINT_act.hour := 0;
        END_IF;
        
        // RESET day counter : (rücksetzen Zähler aktueller Tag
        IF SysClk.evtDAY THEN 
           udt_cntDINT_old.day := udt_cntDINT_act.day;
           udt_cntDINT_act.day := 0;
        END_IF;
        
        // RESET week counter : (rücksetzen Zähler aktuelle Woche
        IF SysClk.evtWEEK THEN 
           udt_cntDINT_old.week := udt_cntDINT_act.week;
           udt_cntDINT_act.week := 0;
        END_IF;
        
        // RESET month COUNTER : (rücksetzen Zähler aktuelles Monat
        IF SysClk.evtMONTH THEN 
           udt_cntDINT_old.month := udt_cntDINT_act.month;
           udt_cntDINT_act.month := 0;
        END_IF;
        
        // RESET year COUNTER : (rücksetzen Zähler aktuelles Jahr
        IF SysClk.evtYEAR THEN 
           udt_cntDINT_old.year := udt_cntDINT_act.year;
           udt_cntDINT_act.year := 0;
        END_IF;
        
    END_IF;

    // Reset user COUNTER (z.B. Schichtzähler)
    IF RESET_USER_COUNTER THEN 
       udt_cntDINT_old.user := udt_cntDINT_act.user;
       udt_cntDINT_act.user := 0;
       
       udt_cntDINT_old.all := udt_cntDINT_act.all;
       
       udt_cntDINT_old.DT_USER_RESET := udt_cntDINT_act.DT_USER_RESET;
       
       iRET:=READ_CLK(CDT := SYS_CLK);  // READ SystemClock SFC1    
       udt_cntDINT_act.DT_USER_RESET := SYS_CLK;
    END_IF;

    IF COUNT THEN
        udt_cntDINT_act.all  := udt_cntDINT_act.all  + dSTEP;
        udt_cntDINT_act.hour := udt_cntDINT_act.hour + dSTEP;
        udt_cntDINT_act.day  := udt_cntDINT_act.day  + dSTEP;
        udt_cntDINT_act.week := udt_cntDINT_act.week + dSTEP;
        udt_cntDINT_act.month:= udt_cntDINT_act.month+ dSTEP;
        udt_cntDINT_act.year := udt_cntDINT_act.year + dSTEP;
        udt_cntDINT_act.user := udt_cntDINT_act.user + dSTEP;
        
        // Überlaufkorrektur bei [hh:mm] oder [mm:ss] Zählung
        IF cfg_cnt_mmss THEN
            IF udt_cntDINT_act.all   MOD 100 >= 60 THEN udt_cntDINT_act.all  := udt_cntDINT_act.all  + 40; END_IF;
            IF udt_cntDINT_act.hour  MOD 100 >= 60 THEN udt_cntDINT_act.hour := udt_cntDINT_act.hour + 40; END_IF;    
            IF udt_cntDINT_act.day   MOD 100 >= 60 THEN udt_cntDINT_act.day  := udt_cntDINT_act.day  + 40; END_IF;    
            IF udt_cntDINT_act.week  MOD 100 >= 60 THEN udt_cntDINT_act.week := udt_cntDINT_act.week + 40; END_IF;     
            IF udt_cntDINT_act.month MOD 100 >= 60 THEN udt_cntDINT_act.month:= udt_cntDINT_act.month+ 40; END_IF;     
            IF udt_cntDINT_act.year  MOD 100 >= 60 THEN udt_cntDINT_act.year := udt_cntDINT_act.year + 40; END_IF;     
            IF udt_cntDINT_act.user  MOD 100 >= 60 THEN udt_cntDINT_act.user := udt_cntDINT_act.user + 40; END_IF;     
        END_IF;
        
    END_IF;

   // OK := TRUE;   // wird von SCL-Compiler bereits durchgeschleift

END_FUNCTION
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Eine Variable relativ hochzählen und in jeweils Monate aufteilen.
(...)
Dann den nächsten Counter hochzählen und den Wert wieder in ein DB sichern.
Was meinst Du mit "relativ hochzählen"?

Warum so umständlich mit mehreren Zählern zählen? Du könntest Deine(n) Zähler in der SPS genauso realisieren wie reale Zähler ohne SPS. Ein üblicher Verbrauchszähler beginnt nicht jeden Monat neu zu zählen, sondern das ist ein einziger fortlaufender Zähler, und wenn jemanden die Monats-Verbräuche interessieren, dann muß halt beim Monatswechsel der Zählerstand notiert werden (in einen remanenten DB gespeichert werden). Der Verbrauch eines Monats ist der Zählerstand vom Monatswechsel am Ende des Monats Minus Zählerstand am Anfang des Monats. Was man dabei beachten sollte, ist daß man einen Zähler mit sooo vielen Ziffern verwendet, daß der möglichst selten überläuft, und wenn er mal überläuft dann sollte er an ganzen Zehnerpotenzen überlaufen. Und ein Verbrauchszähler sollte möglichst ganze Einheiten von etwas "zählen" (als Ganzzahl-Datentyp), weil das fortlaufende Addieren "krummer" Einheiten im Datentyp REAL oder LREAL nur mit relativ wenigen Ziffern funktioniert.

Harald
 
Der Zähler kann bei Betriebsstunden nicht so hoch werden. Ein INT reicht völlig aus. Wenn man die Version von PN/DP Harald nimmt, so müsste man über einen Datumsanzeiger den Sprung zwischen den Monaten ermitteln und dann den aktuellen Wert subtrahieren. Also einfach durchgehend zählen und die Werte zu Monatsbeginn & Monatsende erfassen und im DB ablegen.
Die Version von Magic probiere ich mal aus.
 
Der Zähler kann bei Betriebsstunden nicht so hoch werden. Ein INT reicht völlig aus.
Was genau willst Du denn zählen? Die Betriebsstunden? Zählst Du da nicht die Betriebssekunden sondern wie?

Monatswechsel erkennen: in jedem Zyklus die Systemuhr abfragen, und wenn sich der Monat ändert dann ist ein Monatswechsel. Vorsichtshalber noch eine Ausschaltverzögerung von 90 Sekunden als Verriegelung drauf, damit bei unglücklicher Uhrzeitsynchronisation nicht mehrmals der Monat wechselt.

Um welche SPS-CPU geht es bei Dir eigentlich?

Harald
 
ich hab ein Byte, da stehen die ganzen Ereignisse drin. Bei mir ist das immer MB7

SYSCLK_evtSEC M 7.0 BOOL Systemuhr: Ereignis Sekunde
SYSCLK_evtMIN M 7.1 BOOL Systemuhr: Ereignis Minute
SYSCLK_evtHOUR M 7.2 BOOL Systemuhr: Ereignis Stunde
SYSCLK_evtDAY M 7.3 BOOL Systemuhr: Ereignis Tag
SYSCLK_evtWEEK M 7.4 BOOL Systemuhr: Ereignis Woche
SYSCLK_evtMONTH M 7.5 BOOL Systemuhr: Ereignis Monat
SYSCLK_evtYEAR M 7.6 BOOL Systemuhr: Ereignis Jahr
SYSCLK_RESERVE M 7.7 BOOL Systemuhr: Reserve

hier der Code für SysClk-Events in Step7 classic. In TIA hab ich den einfach konvertiert
und minimal angepasst. Aus TIA bekomm ich den Baustein aber leider nicht mehr als
Quelle raus.

Code:
FUNCTION_BLOCK "m7d_SYSCLK_EVENTS"
TITLE =Standard SystemClock EVENTS
//Fires an 1 cycle Event when the SystemClock changes it's values.
//Provides each event as single BOOL and all combined as evtFLAGs in 
//a Byte.
//
//[IN]
//StartWeekOnSunMon : Start week on Sunday/Monday (configuration input)
//                       FALSE:= week start on Sunday
//                       TRUE := week starts on Monday
//
//[OUT]
//EVENTS as BOOL:
//evt_sec :  Second changed
//evt_min :  minute changed
//evt_hour : hour changed
//evt_day :  day changed
//evt_week : week changed (on sunday or monday as configured
//           with 'StartWeekOnMonday'
//evt_month: month changed
//evt_year:  year changed
//
//evtFLAGs : Events combined as byte
//            Bit 0: evt_sec
//            ...
//            Bit 6: evt_year
//            Bit 7: not used (FALSE)
//
//AUTOR: S.Maag
//DATUM: 7/2013

//
//AENDERUNGSVERMERKE:
//--------------------------------------------------------------------------------
//DATUM        NAME        AENDERUNG
//--------------------------------------------------------------------------------
//16.10.2016   S.Maag     fatalen Fehler P##Buffer Berechnung behoben:
//                        Falsche Bereichskennung ausgeblendet. FB funktionierte
//                        nicht mit Instanz-DB Aufruf, nur über Multiinstanz.
//
//                        Bereichskennung muss aus rel. Adresse aus AR2
//                        entfernt werden, da diese 84 DB ist. Wir benötigen
//                        aber 85 Instanz-DB.
//                        Code getestet mit FB1141: direkter Instanz-DB und
//                        Multinstanz mit 2x Verschachtelung. Funktion I.O. 
//
//20.05.2014   S.Maag     Fehler WeekChange behoben, da ms-Anteil vom falschen
//                        Datensatz (memSYSCLK_BCD) entfernt wurde, konnte
//                        der Wochentag von SYSCLK_BCD nicht erkannt werden. 
//--------------------------------------------------------------------------------
//
//HINWEISE:
AUTHOR : 'S.Maag'
FAMILY : Maagic7
VERSION : 0.1


VAR_INPUT
  StartWeekOnSunMon : BOOL ;    //StartWeekOn Sunday/Monday: FALSE: week starts sunday; TRUE: week starts monday
END_VAR
VAR_OUTPUT
  evt_sec : BOOL ;    //Event second changed
  evt_min : BOOL ;    //Event minute changed
  evt_hour : BOOL ;    //Event hour changed
  evt_day : BOOL ;    //Event day changed
  evt_week : BOOL ;    //Event weed changed
  evt_month : BOOL ;    //Event month changed
  evt_year : BOOL ;    //Event year changed
  evtFLAGs : BYTE ;    //Event flags as byte: bit0: evt_sec .. bit6: evt_year
END_VAR
VAR
  memSYSCLK_BCD : STRUCT     //Speicher letzter Wert SystemUhr BCD Format
   year : BYTE ;    
   month : BYTE ;    
   day : BYTE ;    
   hour : BYTE ;    
   min : BYTE ;    
   sec : BYTE ;    
   ms_1 : BYTE ;    
   weekday : BYTE ;    //Millisekunden ohne Einerstelle
  END_STRUCT ;    
  FP_evt_sec : BOOL ;    
  FP_evt_min : BOOL ;    
  FP_evt_hour : BOOL ;    
  FP_evt_day : BOOL ;    
  FP_evt_week : BOOL ;    
  FP_evt_month : BOOL ;    
  FP_evt_year : BOOL ;    
END_VAR
VAR_TEMP
  DATE_TIME : DATE_AND_TIME ;    
  iRET : INT ;    
  tmpFirstWeekday : INT ;    
  SYSCLK_BCD : STRUCT     
   year : BYTE ;    
   month : BYTE ;    
   day : BYTE ;    
   hour : BYTE ;    
   min : BYTE ;    
   sec : BYTE ;    
   ms_1 : BYTE ;    
   weekday : BYTE ;    
  END_STRUCT ;    
END_VAR
BEGIN
NETWORK
TITLE =Copy system clock to temp memory
//Das DATE_AND_TIME Format benötigt 8 Byte im BCD-Format
//Der Wertebereich geht von DT#1990-1-1-0:0:0.0 bis DT#2089-12-31-23:59:59.999 
//
//BYTE 0:  Jahr    (year)
//BYTE 1:  Monat   (month)
//BYTE 2:  Tag     (day)
//BYTE 3:  Stunde  (hour)
//BYTE 4:  Minute  (minute)
//BYTE 5:  Sekunde (second)
//BYTE 6:  100er und 10er Stelle von Millisekunden
//BYTE 7:  Bit 4..7: 1er  Stelle von Millisekunden
//BYTE 7:  Bit 0..3: Wochentag (1=Sonntag, 2=Montag, ..., 7=Samstag)
      CALL "READ_CLK" (
           RET_VAL                  := #iRET,
           CDT                      := #DATE_TIME);
      NOP   0; 
NETWORK
TITLE =copy the system clock into a useable CLOCK-Structure
//System DateTime auf verwertbare Struktur kopieren
      CALL "BLKMOV" (
           SRCBLK                   := #DATE_TIME,
           RET_VAL                  := #iRET,
           DSTBLK                   := #SYSCLK_BCD);
      NOP   0; 
NETWORK
TITLE =remove milliseond information from weekday
//Wochtag ist nur Bit 0..3
//
//20.05.2012 S.Maag (Fehler behoben, war memSYSCLK_BCD)


      L     #SYSCLK_BCD.weekday; 
      L     W#16#F; 
      UW    ; 
      T     #SYSCLK_BCD.weekday; 
      NOP   0; 
NETWORK
TITLE =Event second changed

      U(    ; 
      L     #SYSCLK_BCD.sec; 
      L     #memSYSCLK_BCD.sec; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_sec; 
      =     #evt_sec; 
NETWORK
TITLE =Event minute changed

      U(    ; 
      L     #SYSCLK_BCD.min; 
      L     #memSYSCLK_BCD.min; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_min; 
      =     #evt_min; 
NETWORK
TITLE =Event hour changed

      U(    ; 
      L     #SYSCLK_BCD.hour; 
      L     #memSYSCLK_BCD.hour; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_hour; 
      =     #evt_hour; 
NETWORK
TITLE =Event day changed

      U(    ; 
      L     #SYSCLK_BCD.day; 
      L     #memSYSCLK_BCD.day; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_day; 
      =     #evt_day; 
NETWORK
TITLE =Event month changed

      U(    ; 
      L     #SYSCLK_BCD.month; 
      L     #memSYSCLK_BCD.month; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_month; 
      =     #evt_month; 
NETWORK
TITLE =Event year changed
//weekday:
//
//1 : sunday
//2 : monday
//3 : thuesday
//4 : wednesday
//5 : thursday
//6 : friday
//7 : saturday
      U(    ; 
      L     #SYSCLK_BCD.year; 
      L     #memSYSCLK_BCD.year; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_year; 
      =     #evt_year; 
NETWORK
TITLE =Select no of first weekday according to 'StartWeekOnSunMon'
//IF StartWeekOnSunMon=TRUE THEN 
//  FirstWeekDay = 2  // Monday
//ELSE
//  FirstWeekDay = 1  // Sunday
//ENDIF
      U(    ; 
      L     1; 
      T     #tmpFirstWeekday; 
      SET   ; 
      SAVE  ; 
      CLR   ; 
      U     BIE; 
      )     ; 
      U     #StartWeekOnSunMon; 
      SPBNB _001; 
      L     2; 
      T     #tmpFirstWeekday; 
_001: NOP   0; 
NETWORK
TITLE =Event week changed
//Fires the WeekChange_Event on sunday or monday (at midnight)
      U(    ; 
      L     #SYSCLK_BCD.weekday; 
      L     #tmpFirstWeekday; 
      ==I   ; 
      )     ; 
      U(    ; 
      L     #SYSCLK_BCD.weekday; 
      L     #memSYSCLK_BCD.weekday; 
      <>I   ; 
      )     ; 
      FP    #FP_evt_week; 
      =     #evt_week; 
NETWORK
TITLE =save the last clock state in static memory

      CALL "BLKMOV" (
           SRCBLK                   := #SYSCLK_BCD,
           RET_VAL                  := #iRET,
           DSTBLK                   := #memSYSCLK_BCD);
      NOP   0; 
NETWORK
TITLE =Transfer the single events into output byte
//ACHTUNG: indirektes kopieren der Daten von Multiinstanzen über Pointer
//ist nur mit Berücksichtigung des absoluten Instanz-Pointers möglich, da 
//die P#-Funktion von Step7 den Pointer nur relativ zu den Daten der aktuelen 
//Instanz ermittelt. Dieser relative Pointer muss zum Instanzpointer aus AR2 
//addiert werden.
//18.10.2016 S.Maag 
//Achtung: Es muss zuerst das Adressregister 2 geladen werden und von dort die 
//Bereichskennung ausgeblendet: TAR2 liefert immer Bereichskennung 84 des 
//Datenbausteins. P##evt_sec liefert 85 "Instanzdatenbaustein als 
//Bereichskennung", was direkt die Instanzdaten adressiert.
//
      TAR2  ; // load akku with instance pointer
      UD    DW#16#FFFFFF; // Bereichskennung (84 "DB") ausblenden 
      L     P##evt_sec; // load relative pointer of EVENT-Outputs
      +D    ; // add instance and relative data pointer
      LAR1  ; // load it into AR1
      L     B [AR1,P#0.0]; // load the 8 BOOL outputs as complete byte
      T     #evtFLAGs; // transfer the EVENTS into the byte 'evtFLAGs
NETWORK
TITLE =

      SET   ; 
      SAVE  ; 
      CLR   ; 
END_FUNCTION_BLOCK

Wenn es um komplette Jahresarchive für Zählerablesungen z.B. Strom oder Wärmeverbräuche, dann geht das mit
dem einfachen Statistikzähler nicht.

Für die Jahresarchive ist das etwas komplexer, dafür müsste ich erst ein Demoprojekt erstellen, sonst kann man
das ohne Vorahnung nicht verwenden. Die Jahresarchive erlauben tägliche und monatliche Ablesungen zu
automatisieren und die Werte einzeln für 366 Tage und 12 Monate zu speichern. Am Jahresende dann automatisch
das archiv sichern, so dass in der CPU immer noch alle Werte vom letzten Jahr vorhanden sind.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
da immer noch was fehlt hab ich mal schnell eine Demo zusammekopiert,
welche die Verwendung des Statistikzählers und vollständiger Jahresarchive zeigt.

Das ist zwar alles S7 classic sollte sich aber einigermaßen auf TIA konvertieren lassen.

Anhang anzeigen 54126
Vielen Dank. Ich migriere das in Tia hoch und berichte dann ob es funktioniert.
 
Zurück
Oben