FUNCTION "FC_RateLimiter" : Void
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      analog_input : Real;           // Raw input signal (setpoint or feedback)
      pos_slew_rate : Real := 10.0;  // Max allowed increase per PLC scan (engineering units)
      neg_slew_rate : Real := 10.0;  // Max allowed decrease per PLC scan (engineering units)
      enable : Bool := TRUE;         // Enable rate limiting
      reset : Bool := FALSE;         // Reset: output snaps to input immediately
   END_VAR

   VAR_OUTPUT 
      rate_limited_output : Real;    // Slew-rate limited output
   END_VAR

   VAR 
      prev_output : Real;            // Previous output value (retains between scans)
      initialized : Bool := FALSE;   // First-run initialization flag
   END_VAR

   VAR_TEMP 
      delta : Real;                  // Calculated change from previous output
      max_rise : Real;               // Clamped positive limit for this cycle
      max_fall : Real;               // Clamped negative limit for this cycle
   END_VAR

BEGIN
	// Handle reset: snap output to input immediately
	IF #reset THEN
		#rate_limited_output := #analog_input;
		#prev_output := #analog_input;
		#initialized := TRUE;
		RETURN;
	END_IF;
	
	// Bypass if disabled: pass-through mode
	IF NOT #enable THEN
		#rate_limited_output := #analog_input;
		#prev_output := #analog_input;
		RETURN;
	END_IF;
	
	// Initialize on first run to avoid startup transient
	IF NOT #initialized THEN
		#rate_limited_output := #analog_input;
		#prev_output := #analog_input;
		#initialized := TRUE;
		RETURN;
	END_IF;
	
	// Ensure slew rates are non-negative
	#max_rise := ABS(#pos_slew_rate);
	#max_fall := ABS(#neg_slew_rate);
	
	// Calculate desired change
	#delta := #analog_input - #prev_output;
	
	// Apply asymmetric slew limiting
	IF #delta > #max_rise THEN
		// Rising too fast: limit to max rise
		#rate_limited_output := #prev_output + #max_rise;
	ELSIF #delta < -#max_fall THEN
		// Falling too fast: limit to max fall
		#rate_limited_output := #prev_output - #max_fall;
	ELSE
		// Within allowed slew range: pass through
		#rate_limited_output := #analog_input;
	END_IF;
	
	// Store for next scan
	#prev_output := #rate_limited_output;
	
END_FUNCTION