FUNCTION_BLOCK "FB_SetAlarmAndLog"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      stSettings : Struct   // Grundsätzliche einstellungen des Bausteins
         sFctnName : String[20];   // Name der Funktion die den Fehler wirft
         nNoMaxErrors : UInt := 32;   // Maximale Anzahl an Fehlern. Werden über "xPlcReOrg" zugewiesen.
         nType : Int := 0;   // 0: IntError | 1: Error | 2: Warning | 3: Information | 4: Debug
      END_STRUCT;
      xInAlarm : Bool;   // An: Fehler liegt an. | Aus: Kein Fehler liegt an
      nInAlarmPriority : USInt := 1;   // Priorität des Fehlers: 1 = Höchste Prio. | 30 = Niedrigste Prio.
      nInAlarmNumber : UInt;   // Fehlernummer von 0 bis (stSettings.nNoMaxErrors - 1)
      sInPrefix : String[20];   // Text vor der Fehlermeldung
      sInAlarmMessage : String[20];   // Text der Fehlermeldung
      sInSuffix : String[20];   // Text nach der Fehlermeldung
      wInTextListId_Message : Word;
      wInTextListId_Function : Word;
      wInTextListId_Prefix : Word;
      wInTextListId_Suffix : Word;
      wInTextListId_Separator : Word;
   END_VAR

   VAR_OUTPUT 
      xOutRdy : Bool;   // Zeigt an, ob ein neuer Fehler geschrieben werden darf
   END_VAR

   VAR_IN_OUT 
      stInOutControlUnitCabinet : "UDT_MainState";
   END_VAR

   VAR RETAIN
      nAlarmNoOffset : UInt;
   END_VAR
   VAR 
      wStatus : Word;
      xAlarmFlag : Bool;
      sErrorString : String;
      xSetProgramAlarm : Bool;
      fbProgram_Alarm {InstructionName := 'Program_Alarm'; LibVersion := '1.0'} : Program_Alarm;
      xRetryProgramAlarm : Bool;
   END_VAR

   VAR_TEMP 
      xTemp : Bool;
      tStringParts : Struct
         sType : String;
         sNumber : String;
         sFunctionName : String;
      END_STRUCT;
      tString : String;
      tSetDataLog : Bool;
      tRunProgramAlarm : Bool;
      tRenewString : Bool;
      tDataLogBuffer : "DataLogBuffer";
      tRisingAlarm : Bool;   // Kommende Meldung
      tFallingAlarm : Bool;   // Gehende Meldung
   END_VAR


BEGIN
	(*
	Der Baustein gibt über den Befehl "Program_Alarm" fehler am HMI aus. Achtung, nur jeweils ein Fehler pro Monitoring-Baustein gleichzeitig.
	
	Aufbau Fehlermeldung:
	Fehler 123:                 Heber 2,                Step 230 -      Heber nicht am Sensor erkannt   BG6061
	"sTypeAndNumber: "          "sFunctionName"       "sInPrefix"     "sInErrorMessage"               "xInSuffix"
	nType&(nErrorNumber+Offset)
	*)
	
	(* Fragen:
	Todo:
	- Priority-Level
	*)
	
	REGION Alarmnummer abfragen
	(*    IF "DB_AlarmGlobal".xAlarmReOrg THEN
	        #nAlarmNoOffset := "FC_GetAlarmNo_1"(#stSettings.nNoMaxErrors);
	        //            #xPlcReOrg := FALSE; // wird wegen "Funktionsname holen" nicht benötigt
	    ELSIF
	        #nAlarmNoOffset = 0 THEN
	        "DB_AlarmGlobal".xAlarmReOrgNextStep := TRUE;
	    END_IF;
	    *)
	    #nAlarmNoOffset := "FC_GetAlarmNo"(#stSettings.nNoMaxErrors);
	END_REGION
	
	REGION Kommende und gehende Meldung erfassen
	    #tRisingAlarm := #xInAlarm AND NOT #xAlarmFlag;
	    #tFallingAlarm := NOT #xInAlarm AND #xAlarmFlag;
	END_REGION
	
	
	REGION Alarm und Datalog aktivieren
	    IF #nAlarmNoOffset > 0 THEN
	        // Error
	        CASE #stSettings.nType OF
	            0..1:  // (0)Intern Error and (1)Error
	                IF #tRisingAlarm OR #xRetryProgramAlarm THEN
	                    #tRunProgramAlarm := TRUE;
	                    #xSetProgramAlarm := TRUE;
	                    #tRenewString := TRUE;
	                    #tSetDataLog := TRUE;
	                    #xRetryProgramAlarm := FALSE;
	                ELSIF #tFallingAlarm OR #xRetryProgramAlarm THEN
	                    #tRunProgramAlarm := TRUE;
	                    #xSetProgramAlarm := FALSE;
	                    #tRenewString := FALSE;
	                    #tSetDataLog := FALSE;
	                    #xRetryProgramAlarm := FALSE;
	                END_IF;
	                
	                // #tRunProgramAlarm := (#xInAlarm <> #xAlarmFlag);//OR #stMachineState.xQuitError; //!!!!
	                // #tSetDataLog := #xInAlarm AND NOT #xAlarmFlag;
	                // #tRenewString := #xInAlarm <> #xAlarmFlag;
	                // #xSetProgramAlarm := #xInAlarm;
	                
	            2..4:  // Warning, Information, Debug
	                IF #xInAlarm AND NOT #xAlarmFlag THEN
	                    #tRunProgramAlarm := TRUE;
	                    #xSetProgramAlarm := TRUE;
	                    #tRenewString := TRUE;
	                    #tSetDataLog := TRUE;
	                ELSIF NOT #xInAlarm AND #xAlarmFlag THEN
	                    #tRunProgramAlarm := TRUE;
	                    #xSetProgramAlarm := FALSE;
	                    #tRenewString := FALSE;
	                    #tSetDataLog := FALSE;
	                ELSIF #stInOutControlUnitCabinet.xQuitError THEN
	                    #tRunProgramAlarm := TRUE;
	                    #xSetProgramAlarm := FALSE;
	                    #tRenewString := FALSE;
	                    #tSetDataLog := FALSE;
	                END_IF;
	        END_CASE;
	    END_IF;
	END_REGION
	
	REGION Fehler zu String zusammenfügen
	    IF #tRenewString THEN
	        REGION Type And Number
	            CASE #stSettings.nType OF
	                0:
	                    #tStringParts.sType := 'Int.Fehler';
	                1:
	                    #tStringParts.sType := 'Fehler';
	                2:
	                    #tStringParts.sType := 'Warnung';
	                3:
	                    #tStringParts.sType := 'Info';
	                4:
	                    #tStringParts.sType := 'Debug';
	                ELSE:
	                    #tStringParts.sType := 'WrongNType';
	            END_CASE;
	            #tStringParts.sNumber := UINT_TO_STRING(#nAlarmNoOffset + #nInAlarmNumber);
	            #tStringParts.sNumber[1] := ' '; // Vorzeichen entfernen
	        END_REGION
	        
	        REGION Function Name
	            #tStringParts.sFunctionName := CONCAT(IN1 := ' in ', IN2 := #stSettings.sFctnName);
	        END_REGION
	        
	        REGION Zusammenfügen
	            
	            #tString := CONCAT(IN1 := #tStringParts.sType ,IN2 := #tStringParts.sNumber, IN3 := #tStringParts.sFunctionName, IN4 := ' | ', IN5 := #sInPrefix);
	            
	            IF #sInPrefix <> '' THEN
	                #tString := CONCAT(IN1 := #tString, IN2 := ' | ');
	            END_IF;
	            #tString := CONCAT(IN1 := #tString, IN2 := #sInAlarmMessage);
	            IF #sInAlarmMessage <> '' THEN
	                #tString := CONCAT(IN1 := #tString, IN2 := ' | ');
	            END_IF;
	            #sErrorString := CONCAT(IN1 := #tString, IN2 := #sInSuffix);
	        END_REGION
	        
	        REGION Nummer überprüfen
	            IF #nInAlarmNumber > #stSettings.nNoMaxErrors - 1 OR
	                #nInAlarmNumber < 0 THEN
	                #sErrorString := CONCAT(IN1 := 'Falsche Fehlernummer in Baustein: ', IN2 := #stSettings.sFctnName);
	            END_IF;
	        END_REGION
	    END_IF;
	END_REGION
	
	REGION ProgramAlarm Setzen
	    IF #tRunProgramAlarm THEN
	        #fbProgram_Alarm(SIG := #xSetProgramAlarm,
	                         Status => #wStatus,
	                         Error => #xTemp,
	                         SD_1 := #wInTextListId_Message,
	                         SD_2 := #sInPrefix,
	                         SD_3 := #sInSuffix,
	                         SD_4 := #wInTextListId_Function,
	                         SD_5 := #wInTextListId_Separator);
	    END_IF;
	    
	END_REGION
	
	REGION Datalog schreiben
	    // Es ist nur 1 Datalog pro Zyklus möglich. Deshalb werden Fehler in einen Fehlerbuffer (FIFO) geschrieben und abgearbeitet.
	    // Datalogs werden nur geschrieben wenn die Priorität hoch genug ist.
	    IF #tSetDataLog AND #nInAlarmPriority <= 0 THEN
	        #tDataLogBuffer."Time" := "FC_TimeAsString"();
	        #tDataLogBuffer.AlarmNr := UINT_TO_INT(#nAlarmNoOffset + #nInAlarmNumber);
	        #tDataLogBuffer.AlarmType := #tStringParts.sType;
	        #tDataLogBuffer.Error := CONCAT(IN1 := #sInPrefix, IN2 := ' ', IN3 := #sInAlarmMessage, IN4 := ' ', IN5 := #sInSuffix);
	        #tDataLogBuffer.Function := #stSettings.sFctnName;
	      //  #xTemp := "FC_WriteDatalogToDB"(#tDataLogBuffer);
	    END_IF;
	END_REGION
	
	REGION Ausgänge setzen
	    #xOutRdy := NOT #xInAlarm AND NOT #xAlarmFlag AND #nAlarmNoOffset > 0;
	END_REGION
	
	REGION Flanken
	    #xAlarmFlag := #xInAlarm;
	END_REGION
	
	REGION Fehler in ProgramAlarm Bearbeitung
	    IF #xTemp AND ((#wStatus = 16#80C1) OR (#wStatus = 16#80C2) OR (#wStatus = 16#80C3) OR (#wStatus = 16#80C4)) THEN
	        #xRetryProgramAlarm := TRUE;
	    END_IF;
	END_REGION
END_FUNCTION_BLOCK

