FUNCTION_BLOCK "FB_ScanMonitor"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      t_InNoProgressTime : Time;
   END_VAR

   VAR_OUTPUT 
      n_OutCurrentBarcode : DInt;
      x_OutNoProgressTimeout : Bool;
      x_OutAssignNewDest : Bool;
   END_VAR

   VAR_IN_OUT 
      udt_Scan_Result : "UDT_MFRScanResult";
   END_VAR

   VAR 
      n_PrevBarcode : DInt;
      rtrig_RtCarrier {InstructionName := 'R_TRIG'; LibVersion := '1.0'; S7_SetPoint := 'False'} : R_TRIG;
      ftrig_FtCarrier {InstructionName := 'F_TRIG'; LibVersion := '1.0'} : F_TRIG;
      rtirg_RtChanged {InstructionName := 'R_TRIG'; LibVersion := '1.0'} : R_TRIG;
      ton_NoProg {InstructionName := 'TON_TIME'; LibVersion := '1.0'} : TON_TIME;
      x_Changed { S7_SetPoint := 'True'} : Bool;
   END_VAR


BEGIN
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	// Flanken auf Präsenz
	#rtrig_RtCarrier(CLK := #udt_Scan_Result.xCarrierPresent);
	#ftrig_FtCarrier(CLK := NOT #udt_Scan_Result.xCarrierPresent);
	
	// Barcode-Änderung (einfacher String-Vergleich)
	#x_Changed := (#udt_Scan_Result.nBarcode <> #n_PrevBarcode);
	
	// Rising-Edge auf Änderung
	#rtirg_RtChanged(CLK := #x_Changed);
	
	// Beim Eintritt in die Zelle: sauber initialisieren
	IF #rtrig_RtCarrier.Q THEN
	    #n_PrevBarcode := 0;            // zwingt erste Übernahme
	    #n_OutCurrentBarcode := 0;
	    #x_OutNoProgressTimeout := FALSE;
	    #x_OutAssignNewDest := FALSE;
	    #ton_NoProg(IN := FALSE,
	                PT := #t_InNoProgressTime);
	END_IF;
	
	// Beim Verlassen: aufräumen
	IF #ftrig_FtCarrier.Q THEN
	    #ton_NoProg(IN := FALSE,
	                PT := #t_InNoProgressTime);
	    #x_OutNoProgressTimeout := FALSE;
	    #x_OutAssignNewDest := FALSE;
	END_IF;
	
	// Neue Barcode-Übernahme und Timer-Reset bei JEDER Änderung (nur wenn Carrier da)
	IF #udt_Scan_Result.xCarrierPresent AND #rtirg_RtChanged.Q THEN
	    #n_PrevBarcode := #udt_Scan_Result.nBarcode;
	    #n_OutCurrentBarcode := #udt_Scan_Result.nBarcode;
	    
	    // Timer neu starten
	    #ton_NoProg(IN := FALSE,
	                PT := #t_InNoProgressTime);   // kurzer Reset
	END_IF;
	
	// Timer-Bedingungen:
	// läuft nur, wenn Carrier da ist UND ein nicht-leerer Barcode anliegt
	IF #udt_Scan_Result.xCarrierPresent AND (#n_OutCurrentBarcode <> 0) THEN
	    #ton_NoProg(IN := TRUE,
	                PT := #t_InNoProgressTime);
	ELSE
	    #ton_NoProg(IN := FALSE,
	                PT := #t_InNoProgressTime);
	END_IF;
	
	// Aufruf des TON
	#ton_NoProg(IN := #ton_NoProg.IN, PT := #t_InNoProgressTime);
	
	// Timeout-Status
	// 1-Zyklus-Impuls erzeugen, wenn Q auf TRUE geht
	IF (NOT #x_OutNoProgressTimeout) AND #ton_NoProg.Q THEN
	    #x_OutAssignNewDest := TRUE;      // Puls
	ELSE
	    #x_OutAssignNewDest := FALSE;
	END_IF;
	
	#x_OutNoProgressTimeout := #ton_NoProg.Q;
	
	// Nach dem Impuls Timer stoppen, damit kein Dauerfeuer
	IF #x_OutAssignNewDest THEN
	    #ton_NoProg(IN := FALSE,
	                PT := #t_InNoProgressTime);
	END_IF;
END_FUNCTION_BLOCK

