%% File: tdelaylib.tlc
%% Abstract:
%%   Transport and Variable Transport delay block library routines
%%
%% $Revision: 1.7.4.7.2.1 $
%% Copyright 1994-2003 The MathWorks, Inc.


%% Function: FcnTDelayTypeSetup ================================================
%% Abstract:
%%      Have mdlhdr.tlc include rtlibsrc.h because we need rt_TDelayInterpolate.
%%
%function FcnTDelayTypeSetup(block, system) void
  %assign ::CompiledModel.IncludeLibsrc = 1
  %if isRSim
    %<LibAddToModelSources("rt_tdelayacc")>
    %openfile tmpBuf 
    %if ::GenCPP
    #ifdef __cplusplus
    extern "C" {
    #endif
    %endif
    extern void * utMalloc(size_t);
    extern void   utFree(void *);
    %if ::GenCPP
    #ifdef __cplusplus
    }
    #endif
    %endif
    %closefile tmpBuf
    %<LibCacheExtern(tmpBuf)>
  %endif
%endfunction


%% Function: FcnTDelayStart ====================================================
%% Abstract:
%%   Start code for the Transport Delay and Variable Transport delay blocks.
%%
%%     o) Initialize the work vectors
%%
%function FcnTDelayStart(block, system) Output
  %assign isRSIM = isRSim
  /* %<Type> Block: %<Name> */
  {
    %assign blockWidth = LibBlockOutputSignalWidth(0)
    %%
    %% Initialize
    %%
    %% For RSIM, we need to allocate the pBuffer for 
    %% each element in the output port separately. This
    %% is because if we need to grow the buffer, then we
    %% we will free the memory associated with the buffer
    %% so there can't be a single buffer for the block.
    %if !isRSIM
      real_T *pBuffer = &%<LibBlockRWork(TUbufferArea, "", "", 0)>;
    %endif
    
    %% Do not change this value without updating the
    %% code below for allocating pbuffer
    %assign localRollThreshold = 2
    
    %%
    %% Declare a local constant buffer size array and/or initial input
    %% array if needed (when rolling)
    %%
    %assign useConstBufSzVect  = 0
    %assign useConstInitInputVect = 0

    %if WILL_ROLL(RollRegions, localRollThreshold)
      %%
      %% Buffer size array for roller
      %%
      %if SIZE(ParamSettings.BufferSize,1) > 1
        %<LibGenConstVectWithInit(ParamSettings.BufferSize,tSS_INT32,"bufSz")>
	%assign useConstBufSzVect = 1
      %endif

      %%
      %% Initial input array for roller
      %%
      %if SIZE(ParamSettings.InitialInput,1) > 1
	%<LibGenConstVectWithInit(ParamSettings.InitialInput,...
	  tSS_DOUBLE,"initInput")>
	%assign useConstInitInputVect = 1
      %endif
    %endif

    %assign rollVars = ["<iwork>/Tail","<iwork>/Head",...
      "<iwork>/Last","<iwork>/CircularBufSize",...
      "<pwork>/TUbufferPtrs"]

    %roll idx = RollRegions, lcv = localRollThreshold, block, "Roller", rollVars

      %%
      %% Compute buffer size
      %%
      %if lcv == ""
	%assign paramSettingIdx = idx
      %else
	%if idx == 0
	  %assign paramSettingIdx = lcv
	%else
	  %assign paramSettingIdx = "%<lcv> + %<idx>"
	%endif
      %endif

      %if useConstBufSzVect
	%assign bufSz = "bufSz[%<paramSettingIdx>]"
      %else
	%assign bufSz = (SIZE(ParamSettings.BufferSize,1) == 1? ...
	  ParamSettings.BufferSize[0]: ...
	  ParamSettings.BufferSize[paramSettingIdx])
      %endif

      %if useConstInitInputVect
	%assign initInput = "initInput[%<paramSettingIdx>]"
      %else
	%assign initInput = (SIZE(ParamSettings.InitialInput,1) == 1? ...
	  ParamSettings.InitialInput[0]: ...
	  ParamSettings.InitialInput[paramSettingIdx])
      %endif

      %%
      %% Initialize the iwork and pointer work vectors
      %%
      %assign tail = LibBlockIWork(Tail,"",lcv,idx)
      %assign head = LibBlockIWork(Head,"",lcv,idx)
      %assign last = LibBlockIWork(Last,"",lcv,idx)
      %assign bufferSiz = LibBlockIWork(CircularBufSize,"",lcv,idx)
      
      %if isRSIM
        %% We need an extra {} if we aren't rolling to make
        %% sure that we avoid symbol clashing for pbuffer
        %if lcv == ""
          {
        %endif
        real_T *pBuffer = (real_T *)utMalloc(2*%<bufSz>*sizeof(real_T));
        if (pBuffer == NULL) {
          ssSetErrorStatus(%<tSimStruct>, "Memory allocation error");
          return;
        }
      %endif

      %<tail> = 0;
      %<head> = 0;
      %<last> = 0;
      %<bufferSiz> = %<bufSz>;
      %%
      pBuffer[0]        = %<initInput>;
      pBuffer[%<bufSz>] = %<LibGetT()>;
      %%
      %if lcv != ""
	%assign pUbuf = LibBlockPWork(TUbufferPtrs, "", lcv, idx)
	%assign pTbuf = LibBlockPWork(TUbufferPtrs, "", ...
	  "%<lcv>+%<blockWidth>", idx)
      %else
	%assign pUbuf = LibBlockPWork(TUbufferPtrs, "", lcv, idx)
	%assign pTbuf = LibBlockPWork(TUbufferPtrs, "", lcv, idx+blockWidth)
      %endif
      %%
      %<pUbuf> = (void *) &pBuffer[0];
      %<pTbuf> = (void *) &pBuffer[%<bufSz>];

      %% Never do this for RSIM because we allocate separate pbuffers
      %% for each output element.
      %if !isRSIM && ((lcv != "") || (lcv == "" && idx < blockWidth-1))
	%if TYPE(bufSz) == "Number"
	  pBuffer += %<2*bufSz>;
	%else
	  pBuffer += 2*%<bufSz>;
	%endif
      %endif
      
      %if isRSIM && lcv == ""
        }
      %endif  
    %endroll
  }

%endfunction %% End FcnTDelayStart

%% Function: FcnIsReal =========================================================
%% Abstract:
%%   Given a string, does evaluation of this string result in a number.
%%   For example if str = "1.0", then %<str> == 1.0
%%   If str = "a2", then %<str> would be an error.
%%

%%   This function is needed because LibBlockParameter always returns a string,
%%   we need to find out if this string is a number. To do this we use FEVAL
%%   to call a matlab function that parses the resultant type.
%%

%function FcnIsReal(strValue)

  %assign temp = FEVAL("sscanf",strValue,"%f%1s")
  %return SIZE(temp,0) == 1

%endfunction %% FcnIsReal



%% Function: FcnTDelayOutputs ==================================================
%% Abstract:
%%   Compute the outputs for the Transport Delay or Variable Transport Delay:
%%
%%       y = u[t - delay]
%%
%function FcnTDelayOutputs(block, system, variableTDelay) Output

  /* %<Type> Block: %<Name> */
  {
    %assign blockWidth = LibBlockOutputSignalWidth(0)
    %assign inport0Width = LibBlockInputSignalWidth(0)
    %%
    %% If needed, setup static const vectors to capture:
    %%   - what input port 0 signals are discrete
    %%   - what the buffer sizes are
    %% This is done, only if the block will 'roll' and these vectors are
    %% of length greater than 1.
    %%
    %assign useConstIsDiscVect = 0
    %assign localRollThreshold = 2
    %assign tMinusDelayValue  = ""

    %if WILL_ROLL(RollRegions, localRollThreshold)

      %if SIZE(ParamSettings.DiscreteInput,1) > 1
	%<LibGenConstVectWithInit(ParamSettings.DiscreteInput,...
	  tSS_BOOLEAN,"isDiscrete")>
	%assign useConstIsDiscVect = 1
      %endif

    %elseif blockWidth == 1 && !variableTDelay

    %assign delay = LibBlockParameter(DelayTime, "", "", 0)

      %if FcnIsReal(delay)
	%if %<delay> > 0.0
	  %assign tMinusDelayValue = "= simTime - %<delay>"
	%else
	  %assign tMinusDelayValue = "= simTime"
	%endif
      %else
	%assign tMinusDelayValue = "= simTime - %<delay>"
      %endif

    %endif
    %%
    %% Declare local variables
    %%
    real_T **uBuffer = (real_T**)&%<LibBlockPWork(TUbufferPtrs,"","",0)>;
    real_T **tBuffer = (real_T**)&%<LibBlockPWork(TUbufferPtrs,"","",...
      blockWidth)>;
    real_T simTime   = %<LibGetT()>;
    real_T tMinusDelay %<tMinusDelayValue>;
    %if variableTDelay
      real_T appliedDelay;
    %endif
    %%
    %% Produce the output (roll it!)
    %%
    %if variableTDelay
      %assign rollVars = ["u1","y0","<param>/MaximumDelay",...
	"<iwork>/Tail","<iwork>/Head",...
	"<iwork>/Last","<iwork>/CircularBufSize"]
    %else
      %if ParamSettings.DirectFeedThrough == "no"
	%assign rollVars = ["y0","<param>/DelayTime",...
	  "<iwork>/Tail","<iwork>/Head",...
	  "<iwork>/Last","<iwork>/CircularBufSize"]
      %else
	%assign rollVars = ["y0","u0","<param>/DelayTime",...
	  "<iwork>/Tail","<iwork>/Head",...
	  "<iwork>/Last","<iwork>/CircularBufSize"]
      %endif
    %endif

    %roll idx = RollRegions, lcv = localRollThreshold, block, "Roller", rollVars

      %if tMinusDelayValue == ""
	%if variableTDelay
	  %assign delayIn  = LibBlockInputSignal(1, "", lcv, idx)
	  %assign maxDelay = LibBlockParameter(MaximumDelay, "", lcv, idx)
	  %assign delay = "appliedDelay"
	  if ((%<delay>=%<delayIn>) > %<maxDelay>) {
	    %<delay> = %<maxDelay>;
	  }
	%else
	  %assign delay = LibBlockParameter(DelayTime, "", lcv, idx)
	%endif

	%if FcnIsReal(delay)
	  %if %<delay> > 0.0
	    tMinusDelay = simTime - %<delay>;
	  %else
	    tMinusDelay = simTime;
	  %endif
	%else
	  tMinusDelay = ((%<delay> > 0.0) ? %<delay> : 0.0);
	  tMinusDelay = simTime - tMinusDelay;
	%endif
      %endif

      %assign tail = LibBlockIWork(Tail, "", lcv, idx)
      %assign head = LibBlockIWork(Head, "", lcv, idx)
      %assign last = LibBlockIWork(Last, "", lcv, idx)

      %assign y = LibBlockOutputSignal(0, "", lcv, idx)
 
      %if lcv == ""
	%assign paramSettingIdx = idx
      %else
	%if idx == 0
	  %assign paramSettingIdx = lcv
	%else
	  %assign paramSettingIdx = "%<lcv> + %<idx>"
	%endif
      %endif

      %if useConstIsDiscVect
	%assign isDiscrete = "isDiscrete[%<paramSettingIdx>]"
      %else
	%assign isDiscrete = (SIZE(ParamSettings.DiscreteInput,1) == 1? ...
	  ParamSettings.DiscreteInput[0]: ...
	  ParamSettings.DiscreteInput[paramSettingIdx])
      %endif

      %assign bufSz = LibBlockIWork(CircularBufSize, "", lcv, idx)

      %if ( Accelerator || isRSim || ...
	    CodeFormat == "S-Function" || ...
	    SolverType == "VariableStep" || ...
	    IsModelReferenceForASimstructBasedTarget() )
	%assign minorStepAndTAtLastMajorOutput = ...
	  "(boolean_T) " ...
	  "(%<RTMIs("MinorTimeStep")> && " ...
	  "(%<RTMGet("TimeOfLastOutput")> == %<LibGetT()>))"
      %else
	%assign minorStepAndTAtLastMajorOutput = 0
      %endif

      %% The cast to boolean_T above is to avoid MSVC warning C4761:
      %% "integral size mismatch in argument; conversion supplied"
      %if variableTDelay || ParamSettings.DirectFeedThrough == "no"
	%<y> = rt_TDelayInterpolate(
	tMinusDelay,
	*tBuffer,
        *uBuffer,
         %<bufSz>,
        &%<last>,
         %<tail>,
         %<head>,
         %<isDiscrete>,
         %<minorStepAndTAtLastMajorOutput>);
       %else
	 %assign u0 = LibBlockInputSignal(0, "", lcv, idx)
	 %<y> = rt_TransportDelay( 
	tMinusDelay,
	*tBuffer,
        *uBuffer,
         %<bufSz>,
        &%<last>,
         %<tail>,
         %<head>,
         %<isDiscrete>,
         %<minorStepAndTAtLastMajorOutput>,
	 %<u0>,
	 %<delay>);       
       %endif
	 %if (lcv != "") || (lcv == "" && idx < blockWidth-1)
	tBuffer++; uBuffer++;
      %endif
    %endroll
  }

%endfunction %% FcnTDelayOutputs



%% Function: FcnTDelayUpdate ===================================================
%% Abstract:
%%   Update the input history for the Transport Delay or Variable Transport
%%   Delay.
%%
%function FcnTDelayUpdate(block, system, variableTDelay) Output
  /* %<Type> Block: %<Name> */
  {
    %assign blockWidth = LibBlockOutputSignalWidth(0)
    real_T **uBuffer = (real_T**)&%<LibBlockPWork(TUbufferPtrs,"","",0)>;
    real_T **tBuffer = (real_T**)&%<LibBlockPWork(TUbufferPtrs,"","",...
      blockWidth)>;
    real_T simTime   = %<LibGetT()>;

    %assign rollVars = ["u0", "<iwork>/Tail","<iwork>/Head", ...
      "<iwork>/CircularBufSize"]
    %if Accelerator || isRSim
      %if variableTDelay
	%assign delayPrm = "MaximumDelay"
      %else
	%assign delayPrm = "DelayTime"
      %endif
      %assign rollVars = ["u0", "<iwork>/Tail","<iwork>/Head", ...
	"<iwork>/Last", "<iwork>/CircularBufSize", "<param>/%<delayPrm>"]
      %assign maxNewBufSz = LibBlockIWork(MaxNewBufSize, "", "", 0)
    %endif


    %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
      %assign tail  = LibBlockIWork(Tail, "", lcv, idx)
      %assign head  = LibBlockIWork(Head, "", lcv, idx)
      %assign bufSz = LibBlockIWork(CircularBufSize, "", lcv, idx)

      %<head> = ((%<head> < (%<bufSz>-1)) ? (%<head>+1) : 0);

      if (%<head> == %<tail>) {
	%if Accelerator || isRSim
	  %assign last        = LibBlockIWork(Last, "", lcv, idx)
	  %assign tDelay      = LibBlockParameter(%<delayPrm>, "", lcv, idx)
	  %assign tMinusDelay = "simTime - %<tDelay>"
	  if (!rt_TDelayUpdateTailOrGrowBuf( &%<bufSz>, ...
	    &%<tail>, &%<head>, &%<last>, %<tMinusDelay>, ...
	    tBuffer, uBuffer, &%<maxNewBufSz>)) {
	    %<RTMSetErrStat("\"tdelay memory allocation error\"")>;
	  }
	%else
	  %% Handle buffer overflows by loosing oldest data.
	  %<tail> = ((%<tail> < (%<bufSz>-1)) ? (%<tail>+1) : 0);
	%endif
      }

      %if (lcv != "") || (lcv == "" && idx < blockWidth-1)
	%assign pp = "++"
      %else
	%assign pp = ""
      %endif
      (*tBuffer%<pp>)[%<head>] = simTime;
      (*uBuffer%<pp>)[%<head>] = %<LibBlockInputSignal(0, "", lcv, idx)>;

    %endroll
  }
%endfunction %% FcnTDelayUpdate

%% [eof] tdelaylib.tlc
