classdef (Hidden = true) ModelBlockTargetInterface < rtw.pil.TargetInterface   
%MODELBLOCKTARGETINTERFACE creates a Model block target interface file
%
%   MODELBLOCKTARGETINTERFACE creates a Model block target interface file
%   defined by the specified CodeInfo object
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2008-2020 The MathWorks, Inc.

    properties (GetAccess = 'protected', SetAccess = 'private')
       errorStatusRequired;
       errorStatusData;
       stopRequestedRequired;
       stopRequestedData;       
    end              
    
    properties(Access = 'protected')
        AbsTimeData;
    end
    
    methods
        % constructor
        function this = ModelBlockTargetInterface(silPILWrapperUtils, ...
                targetInterfacePath)
            narginchk(2, 2);
            % call super class constructor
            this@rtw.pil.TargetInterface(silPILWrapperUtils, ...
                targetInterfacePath);  
            % init absoluteTimeData
            this.AbsTimeData = this.getAbsoluteTimeData;
        end                
    end
    
    methods (Access = 'protected')                   
        function writeSectionIncludes(this)
            % call super class method
            writeSectionIncludes@rtw.pil.TargetInterface(this); 
            % add extra includes
            if this.codeInfoUtils.isTaskCounterRequired
               % we use memset to zero initialize the XILTimingData
               this.writer.writeLine('#include <string.h>'); 
               this.writer.newLine;
            end                        
        end        
        
        function writeSectionStorageHook(this)                                                                                                        
            % special internalData items
            %
            % MdlRefTIDData
            [mdlrefTIDs, mdlrefTIDValues] = this.getMdlRefTIDData;
            for i=1:length(mdlrefTIDs)
                this.setImplementationOwner(mdlrefTIDs(i).Implementation, 'PIL');
                TID = mdlrefTIDValues{i};
                % for scheduling single In-the-Loop component, globalTID maps
                % directly on to the referenced model TID
                globalTID = TID;
                this.writer.writeLine(['#define mdlref_TID' TID ' ' globalTID]);
            end        
            % ErrorStatus
            if this.errorStatusRequired
                % define error status                
                this.writeSectionErrorStatusStorage;
            end  
            % StopRequested
            if this.stopRequestedRequired
               % define stop requested
               this.writeSectionStopRequestedStorage;
            end
            % Timing Bridge
            [timingBridgeRequired, timingBridgeData] = this.codeInfoUtils.getTimingBridgeInfo;
            if timingBridgeRequired
                % define timing bridge
                this.writeSectionTimingBridgeStorage(timingBridgeData);
            end
            % C-API Child Path
            childPath = this.getChildPath;            
            if ~isempty(childPath)
               this.writeSectionChildPathStorage(childPath); 
            end            
            % C-API Parent MMI
            parentMMI = this.getParentMMI;                      
            if ~isempty(parentMMI)
               this.writeSectionParentMMIStorage(parentMMI); 
            end    
            % internal data
            %
            % data defined above won't be redefined because we set its
            % owner
            this.writeSectionStorageInternalData;
        end        
        
        function writeSectionChildPathStorage(this, childPath)
            % define NULL "rt_ChildPath"
            implementation = childPath.Implementation;
            this.setImplementationOwner(implementation, 'PIL');
            this.writer.writeLine('/* define NULL child path */');
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(implementation.Type);                    
            type_identifier = storageType.Identifier;      
            this.writer.writeLine([type_identifier ...
                                   ' * ' ...
                                   this.getExpression(implementation) ...
                                   ' = (' type_identifier ' *) 0;']);
            this.writer.newLine;
        end
        
        function writeSectionParentMMIStorage(this, parentMMI)                              
            % define NULL parent MMI pointer
            implementation = parentMMI.Implementation;
            this.setImplementationOwner(implementation, 'PIL');
            this.writer.writeLine('/* define NULL parentMMI */');
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(implementation.Type);                    
            type_identifier = storageType.Identifier;                        
            this.writer.writeLine([type_identifier ...
                                   ' * ' ...
                                   this.getExpression(implementation) ...                                   
                                   ' = (' type_identifier ' *) 0;']);                                    
            this.writer.newLine;            
        end
        
        function writeSectionErrorStatusStorage(this)                        
            % define error status as const pointer to pointer to char_T
            %
            % it is not In-the-Loop's responsibility to define storage for the error status
            % that is owned by whatever code sets the error status
            %
            % typically, scheduling code will check for an error by
            % checking for a non-null error status.   In-the-Loop does not
            % check the error status at all, and there is no
            % propagation of error status to the host.
            %
            errorStatus_tgt = 'rt_errorStatus_tgt_var';
            implementation = this.errorStatusData.Implementation;
            this.setImplementationOwner(implementation, 'PIL');
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(implementation.Type);                    
            type_identifier = storageType.Identifier;            
            this.writer.writeLine('/* initialize error status as a null pointer */');
            this.writer.writeLine(['static const ' type_identifier ' * ' errorStatus_tgt ' = ((const ' type_identifier ' *) 0);']);
            this.writer.writeLine(['static const ' type_identifier ' ** ' this.getExpression(implementation) ' = &' errorStatus_tgt ';']);
            this.writer.newLine;          
        end
        
        function writeSectionStopRequestedStorage(this)
            % define stop requested as pointer to boolean_T
            %
            % typically, scheduling code will check for stop requested by
            % via rtmGetStopRequested.  In-the-Loop does not
            % check stop requested at all, and there is no
            % propagation of stop requested to the host.
            %
            stopRequested_tgt = 'rt_stopRequested_tgt_var';
            implementation = this.stopRequestedData.Implementation;
            this.setImplementationOwner(implementation, 'PIL');
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(implementation.Type);
            type_identifier = storageType.Identifier;
            this.writer.writeLine('/* initialize stop requested as false */');
            this.writer.writeLine(['static ' type_identifier ' ' stopRequested_tgt ' = 0;']);
            this.writer.writeLine(['static ' type_identifier ' * ' this.getExpression(implementation) ' = &' stopRequested_tgt ';']);
            this.writer.newLine;
        end
        
        function writeSectionTimingBridgeStorage(this, timingBridgeData)  
            % define the timingBridge and XILTimingData variables
            timingBridge_tgt = 'timingBridge_tgt_var';
            implementation = timingBridgeData.Implementation;
            this.setImplementationOwner(implementation, 'PIL');            
            storageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(implementation.Type);
            type_identifier = storageType.Identifier;                        
            this.writer.writeLine('/* define timingBridge */');
            this.writer.writeLine(['static ' type_identifier ' ' timingBridge_tgt ';']);
            this.writer.writeLine(['static const ' type_identifier ' * ' this.getExpression(implementation) ' = &' timingBridge_tgt ';']);
            this.writer.newLine;
            
            if this.codeInfoUtils.isTaskCounterRequired || ...
                    this.AbsTimeData.XILWrapperProvideClockTicks
                periodicRates = this.codeInfoUtils.getRates('PERIODIC');
                asyncRates =  this.codeInfoUtils.getRates('ASYNCHRONOUS');
                numRates = length(periodicRates) + length(asyncRates);
                
                this.writer.writeLine('/* define Timing structure */');
                this.writer.writeLine('static struct {');
                this.writer.incIndent;
                
                if this.AbsTimeData.XILWrapperProvideClockTicks
                    for rateIdx = 1:length(periodicRates)
                        tid = int2str(rateIdx - 1);
                        if this.AbsTimeData.ClockTicksReqVector(rateIdx)
                            % clockTick and clockTickH are hardcoded to be
                            % uint32_T for model reference in the
                            % timingBridge structure.
                            this.writer.writeLine(['uint32_T clockTick' tid ';']);
                        end
                        if this.AbsTimeData.LongClockTicksReqVector(rateIdx)
                            this.writer.writeLine(['uint32_T clockTickH' tid ';']);
                        end
                    end
                end
                
                if this.codeInfoUtils.isTaskCounterRequired                    
                    this.writer.writeLine('struct {');
                    this.writer.incIndent;
                    this.writer.writeLine(['uint32_T TID[' int2str(numRates) '];']); 
                    this.writer.decIndent;
                    this.writer.writeLine('} TaskCounters;');                    
                    rateInteractions = this.codeInfoUtils.getRateInteractions;
                    if ~isempty(rateInteractions)
                        this.writer.writeLine('struct {');
                        this.writer.incIndent;
                        for i = 1:size(rateInteractions)
                            currInteraction = rateInteractions(i, :);
                            this.writer.writeLine(['boolean_T TID' ...
                                int2str(currInteraction(1)) ...
                                '_' ...
                                int2str(currInteraction(2)) ';']);
                        end
                        this.writer.decIndent;
                        this.writer.writeLine('} RateInteraction;');
                    end
                end
                this.writer.decIndent;
                this.writer.writeLine('} XILTimingData;');
                this.writer.newLine;
            end
            this.writer.writeLine('/* XILFirstInitCond will be updated by xilInitialize and xilSystemInitialize */');
            this.writer.writeLine('static boolean_T XILFirstInitCond = 0;');
            this.writer.newLine;
        end  
        
        function writeSectionXILIOData(this)
           % call superclass method
           writeSectionXILIOData@rtw.pil.TargetInterface(this); 
           % process enable I/O
           this.writeFuncXILIOData(this.codeInfo.EnableFunction, 'enable');
           % process disable I/O
           this.writeFuncXILIOData(this.codeInfo.DisableFunction, 'disable');
           % process init conditions I/O
           this.writeFuncXILIOData(this.codeInfoUtils.getSystemInitializeFunction, 'system_initialize');            
           % process SystemReset I/O
           this.writeFuncXILIOData(this.codeInfo.SystemResetFunction, 'system_reset');           
        end               
        
        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCases(this)
            % call super class method
            commandIdxSwitchGenerated = writeSectionInitUDataProcessingCases@rtw.pil.TargetInterface(this);
                                                                                                
            % The super-class writes the XIL_ENABLE_COMMAND and 
            % XIL_DISABLE_COMMAND case statements for export functions    
            if ~this.codeInfoUtils.isExportFunctions
                % support for EnableFunction inputs
                writeSectionInitUDataProcessingCase(this, ...
                    this.codeInfo.EnableFunction, ...
                    'XIL_ENABLE_COMMAND', ...
                    'xil_fcnid0_enable');
                
                % support for DisableFunction inputs
                writeSectionInitUDataProcessingCase(this, ...
                    this.codeInfo.DisableFunction, ...
                    'XIL_DISABLE_COMMAND', ...
                    'xil_fcnid0_disable');
            end
            
            % support for SystemInitialize inputs
            writeSectionInitUDataProcessingCase(this, ...
                                                this.codeInfoUtils.getSystemInitializeFunction, ...                                               
                                                'XIL_SYSTEM_INITIALIZE_COMMAND', ...
                                                'xil_fcnid0_system_initialize');  
            
            % support for SystemReset inputs
            writeSectionInitUDataProcessingCase(this, ...
                this.codeInfo.SystemResetFunction, ...
                'XIL_SYSTEM_RESET_COMMAND', ...
                'xil_fcnid0_system_reset');
        end
        
        function commandIdxSwitchGenerated = writeSectionInitYDataProcessingCases(this)            
            % call super class method
            commandIdxSwitchGenerated = writeSectionInitYDataProcessingCases@rtw.pil.TargetInterface(this);
                                                
            % support for EnableFunction outputs
            writeSectionInitYDataProcessingCase(this, ...
                this.codeInfo.EnableFunction, ...
                'XIL_ENABLE_COMMAND', ...
                'xil_fcnid0_enable');
            
            % The super-class writes XIL_DISABLE_COMMAND case statements 
            % for export functions
            if ~this.codeInfoUtils.isExportFunctions    
                % support for DisableFunction outputs
                writeSectionInitYDataProcessingCase(this, ...
                    this.codeInfo.DisableFunction, ...
                    'XIL_DISABLE_COMMAND', ...
                    'xil_fcnid0_disable');
            end
            
            % support for SystemInitialize outputs
            writeSectionInitYDataProcessingCase(this, ...
                this.codeInfoUtils.getSystemInitializeFunction, ...
                'XIL_SYSTEM_INITIALIZE_COMMAND', ...
                'xil_fcnid0_system_initialize');            
            
            % support for SystemReset outputs
            writeSectionInitYDataProcessingCase(this, ...
                this.codeInfo.SystemResetFunction, ...
                'XIL_SYSTEM_RESET_COMMAND', ...
                'xil_fcnid0_system_reset');
        end
        
        function hasOnlyLocalAccess = hasOnlyLocalAccess(this, dataInterface)
            if this.codeInfoUtils.isModelArgParam(dataInterface)
                isSingleInst = strcmp(this.SILPILWrapperUtils.getConfigInterface. ...
                    getParam('ModelReferenceNumInstancesAllowed'), 'Single');
                if isSingleInst && this.codeInfoUtils.isGroupedParameter(dataInterface)
                    % for InstP model arguments in single instance code,
                    % xil_interface defines the global param struct that is
                    % accessed directly in the generated code.
                    hasOnlyLocalAccess = false;
                else
                    hasOnlyLocalAccess = true;
                end
            else
                % call super class method
                hasOnlyLocalAccess = hasOnlyLocalAccess@rtw.pil.TargetInterface(this, dataInterface); %#ok<NODEF>
            end
        end
        
        function writeSectionInitBody(this)            
            timingBridgeRequired = this.codeInfoUtils.getTimingBridgeInfo;
            if timingBridgeRequired
                if ~this.codeInfoUtils.isTaskCounterRequired && ~this.AbsTimeData.XILWrapperProvideClockTicks
                    this.writer.writeLine('timingBridge_tgt_var.nTasks = 0;');
                else
                    this.writer.writeLine('/* initialize XILTimingData */');
                    this.writer.writeLine('(void) memset((void *) &XILTimingData, 0, sizeof(XILTimingData));');
                    % get rates
                    numPeriodicRates = length(this.codeInfoUtils.getRates('PERIODIC'));
                    numAsyncRates =  length(this.codeInfoUtils.getRates('ASYNCHRONOUS'));
                    numRates = numPeriodicRates + numAsyncRates;
                    numRatesStr = int2str(numRates);
                    this.writer.writeLine('{');
                    this.writer.incIndent;
                    
                    if this.AbsTimeData.XILWrapperProvideClockTicks
                        if this.AbsTimeData.isClockTicksRequired
                            this.writer.writeLine(['static uint32_T *clockTickPtrs[' int2str(numPeriodicRates) '];']);
                        end
                        if this.AbsTimeData.isLongClockTicksRequired
                            this.writer.writeLine(['static uint32_T *clockTickHPtrs[' int2str(numPeriodicRates) '];']);
                        end
                    end
                    
                    if this.codeInfoUtils.isTaskCounterRequired
                        this.writer.writeLine('static uint32_T *taskCounterPtrs;');
                        % get rate interactions
                        rateInteractions = this.codeInfoUtils.getRateInteractions;
                        if ~isempty(rateInteractions)
                            this.writer.writeLine(['static boolean_T *rateTransitionPtrs[(' ...
                                numRatesStr ' * ' ...
                                numRatesStr ')];']);
                        end
                        
                        % init taskCounter
                        this.writer.writeLine('taskCounterPtrs = &(XILTimingData.TaskCounters.TID[0]);');
                        this.writer.writeLine('timingBridge_tgt_var.taskCounter = taskCounterPtrs;');
                        % init rateTransition
                        for i = 1:size(rateInteractions)
                            currInteraction = rateInteractions(i, :);
                            currI = int2str(currInteraction(1));
                            currJ = int2str(currInteraction(2));
                            this.writer.writeLine(['rateTransitionPtrs[' ...
                                currI '*' numRatesStr ...
                                ' + ' ...
                                currJ '] = &(XILTimingData.RateInteraction.TID' ...
                                currI ...
                                '_' ...
                                currJ ');']);
                        end
                        if ~isempty(rateInteractions)
                            this.writer.writeLine('timingBridge_tgt_var.rateTransition = rateTransitionPtrs;');
                        end
                    end
                    
                    % init clockTicks
                    if this.AbsTimeData.XILWrapperProvideClockTicks
                        for rateIdx = 1:numPeriodicRates
                            if this.AbsTimeData.isClockTicksRequired
                                tid = int2str(rateIdx - 1);
                                if this.AbsTimeData.ClockTicksReqVector(rateIdx)
                                    this.writer.writeLine(['clockTickPtrs[' tid '] = &(XILTimingData.clockTick' tid ');']);
                                else
                                    this.writer.writeLine(['clockTickPtrs[' tid '] = (uint32_T *) 0;']);
                                end
                            end
                            
                            if this.AbsTimeData.isLongClockTicksRequired
                                if this.AbsTimeData.LongClockTicksReqVector(rateIdx)
                                    this.writer.writeLine(['clockTickHPtrs[' tid '] = &(XILTimingData.clockTickH' tid ');']);
                                else
                                    this.writer.writeLine(['clockTickHPtrs[' tid '] = (uint32_T *) 0;']);
                                end
                            end
                        end
                        
                        if this.AbsTimeData.isClockTicksRequired
                            this.writer.writeLine('timingBridge_tgt_var.clockTick = clockTickPtrs;');
                        else
                            this.writer.writeLine('timingBridge_tgt_var.clockTick = (uint32_T **) 0;');
                        end
                        
                        if this.AbsTimeData.isLongClockTicksRequired
                            this.writer.writeLine('timingBridge_tgt_var.clockTickH = clockTickHPtrs;');
                        else
                            this.writer.writeLine('timingBridge_tgt_var.clockTickH = (uint32_T **) 0;');
                        end
                    else
                        this.writer.writeLine('timingBridge_tgt_var.clockTick = (uint32_T **) 0;');
                        this.writer.writeLine('timingBridge_tgt_var.clockTickH = (uint32_T **) 0;');
                    end
                    
                    this.writer.writeLine(['timingBridge_tgt_var.nTasks = ' numRatesStr ';']);
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
                
                % always initialize firstInitCond
                this.writer.writeCellLines({'timingBridge_tgt_var.firstInitCond = &XILFirstInitCond;' ...
                    '/* firstInitCond should be true until after the first InitCond call */' ...
                    'XILFirstInitCond = 1;'});
            end
                       
            % C-API initialization
            if this.isCAPIEnabled
                this.writeSectionInitializeCAPI;                          
            end                        
            
            % call super class method
            writeSectionInitBody@rtw.pil.TargetInterface(this);
        end
        
        function writeSectionInitializeCAPI(this)                        
            % initialize arguments passed to mdl ref initialization
            % function
            %            
            childMMIIdx = this.getChildMMIIdx;
            assert(~isempty(childMMIIdx), ...
                   'ChildMMIIdx must not be empty when C-API is on.');
            % only a single child MMI (the In-the-Loop component)
            childMMIIdxExpr = this.getExpression(childMMIIdx.Implementation);
            this.writer.writeLine([childMMIIdxExpr ' = 0;']);
            cStateIdx = this.getCStateIdx;
            assert(~isempty(cStateIdx), ...
                   'CSTATEIdx must not be empty when C-API is on.');
            % states are not supported
            cStateIdxExpr = this.getExpression(cStateIdx.Implementation);
            this.writer.writeLine([cStateIdxExpr ' = -1;']);
        end                                
        
        function writeSectionSystemInitializeFunction(this)           
            this.writeSectionSystemInitOrResetFunction(this.codeInfoUtils.getSystemInitializeFunction);            
        end
        
        function writeSectionSystemResetFunction(this)
            this.writeSectionSystemInitOrResetFunction(this.codeInfo.SystemResetFunction);           
        end
        
        function writeSectionSystemInitializeTrailer(this)                        
            timingBridgeRequired = this.codeInfoUtils.getTimingBridgeInfo;
            if timingBridgeRequired
                % clear XILFirstInitCond
                this.writer.writeCellLines({'/* firstInitCond should be false after the first InitCond call */' ...
                    'XILFirstInitCond = 0;'});
            end
        end
                
        function writeSectionIncrementClockTicksForSingleTaskingSubRates(this)
            assert(~this.codeInfoUtils.isMultiTasking, ...
                ['writeSectionIncrementClockTicksForSingleTaskingSubRates ',...
                'should only be called for singletasking scheduling']);
            periodicRates = this.codeInfoUtils.getRates('PERIODIC');
            for rateIdx = 2:length(periodicRates)
                tid = int2str(rateIdx - 1);
                this.writer.newLine;
                if this.AbsTimeData.ClockTicksReqVector(rateIdx)
                    this.writer.writeLine(['if (XILTimingData.TaskCounters.TID[' tid '] == 0) {']);
                    this.writer.incIndent;
                    this.writeSectionIncrementClockTicksForTID(rateIdx);
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
            end
        end
        
        function writeSectionIncrementClockTicksForTID(this, taskIdx)
            tid = num2str(taskIdx-1);
            if this.AbsTimeData.ClockTicksReqVector(taskIdx)
                this.writer.writeLine('/* Update absolute time */');
                this.writer.writeLine(['XILTimingData.clockTick' tid '++;']);
            end
            if this.AbsTimeData.LongClockTicksReqVector(taskIdx)
                this.writer.writeLine(['if (!XILTimingData.clockTick' tid ') {']);
                this.writer.incIndent;
                this.writer.writeLine(['XILTimingData.clockTickH' tid '++;']);
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end
        
        function writeSectionOutput(this)
            if this.codeInfoUtils.isTaskCounterRequired
               % generate scheduler
               this.writer.writeLine('static void rate_scheduler(void) {');
               this.writer.incIndent;                              
               % process rate transitions
               rateInteractions = this.codeInfoUtils.getRateInteractions;
               if ~isempty(rateInteractions)
                   this.writer.writeCellLines({'/* To ensure a safe and deterministic data transfer between two rates,';
                       ' * data is transferred at the priority of a fast task and the frequency';
                       ' * of the slow task.  The following flags indicate when the data transfer';
                       ' * happens.  That is, a rate interaction flag is set true when both rates';
                       ' * will run, and false otherwise.';
                       ' */'});
                   this.writer.newLine;                   
                   iColumn = 1;
                   jColumn = 2;
                   tid_I_Vec = unique(rateInteractions(:, iColumn));
                   for i=1:length(tid_I_Vec)
                       tid_I = tid_I_Vec(i);
                       tid_I_str = int2str(tid_I);
                       tid_J_idx = find(rateInteractions(:, iColumn) == tid_I);
                       this.writer.writeLine(['/* tid ' tid_I_str ' shares data with slower tid(s) */']);
                       % no need to check TID 0 task counter - this function
                       % is called from the base rate
                       if tid_I ~= 0
                           this.writer.writeLine(['if (XILTimingData.TaskCounters.TID[' tid_I_str '] == 0) {']);
                           this.writer.incIndent;
                       end
                       for j=1:length(tid_J_idx)
                           tid_J = rateInteractions(tid_J_idx(j), jColumn);
                           tid_J_str = int2str(tid_J);
                           this.writer.writeLine(['/* slow tid: ' tid_J_str ' */']);
                           this.writer.writeLine(['XILTimingData.RateInteraction.TID' ...
                               tid_I_str '_' tid_J_str ' = ']);
                           this.writer.writeLine(['   (XILTimingData.TaskCounters.TID[' tid_J_str '] == 0);']);
                       end
                       if tid_I ~= 0
                           this.writer.decIndent;
                           this.writer.writeLine('}');
                       end
                       this.writer.newLine;
                   end
               end   
               periodicRates = this.codeInfoUtils.getRates('PERIODIC');
               numRates = length(periodicRates);
               if numRates > 1
                   this.writer.writeCellLines({'/* Compute which subrates run during the next base time step.  Subrates';
                       ' * are an integer multiple of the base rate counter.  Therefore, the subtask';
                       ' * counter is reset when it reaches its limit (zero means run).';
                       ' */'});
                   this.writer.newLine;
                   %
                   baseRate = periodicRates(1);
                   for rateIdx = 2:length(periodicRates)
                      currRate = periodicRates(rateIdx);
                      % calculate number of base rates per sub rate,
                      % rounding to avoid floating point issues                      
                      counterReset = uint32(currRate.SamplePeriod / baseRate.SamplePeriod);
                      tid = int2str(rateIdx - 1);                      
                      sampleTimeCExpr = rtw.connectivity.CodeInfoUtils.double2str(currRate.SamplePeriod);
                      sampleOffsetCExpr = rtw.connectivity.CodeInfoUtils.double2str(currRate.SampleOffset);
                      %
                      this.writer.writeLine(['/* sample time: [' sampleTimeCExpr 's, ' sampleOffsetCExpr 's] */']);
                      this.writer.writeLine(['if (++XILTimingData.TaskCounters.TID[' tid '] == ' int2str(counterReset) ') {']);
                      this.writer.writeLine(['   XILTimingData.TaskCounters.TID[' tid '] = 0;']);    
                      this.writer.writeLine('}');
                  end                                      
               end
                                       
               this.writer.decIndent;
               this.writer.writeLine('}');
               this.writer.newLine;
            end            
            % call super class method
            writeSectionOutput@rtw.pil.TargetInterface(this);               
        end
        
        function writeSectionOutputBody(this)
            tid = this.getTIDData;
            if ~isempty(tid)
                % map xilTID to TIDData
                this.writer.writeLine([tid.Implementation.Identifier ' = xilTID;']);
            end
            % call super class method
            writeSectionOutputBody@rtw.pil.TargetInterface(this);                         
        end
        
        function writeSectionOutputFunctionCall(this, taskIdx, outputTask)
            if this.codeInfoUtils.isTaskCounterRequired
                if this.codeInfoUtils.isMultiTasking
                    % multitasking rate scheduler is called before output
                    if taskIdx == 1
                        this.writer.newLine;
                        this.writer.writeLine('/* call scheduler */');
                        this.writer.writeLine('rate_scheduler();');                        
                    end
                end
            end
            
            % call super class method
            writeSectionOutputFunctionCall@rtw.pil.TargetInterface(this, taskIdx, outputTask);
            if isempty(this.codeInfoUtils.getUpdateTasks)
                % combined output & update                
                if this.AbsTimeData.XILWrapperProvideClockTicks
                    if this.codeInfoUtils.isExportFunctions
                        % In Export Function, taskIdx is explicit tasks stored from index 
                        % 2 in ClockTicksReqVector as the first element is an implicit task.
                        % Thus we increment one.
                        this.writeSectionIncrementClockTicksForTID(taskIdx+1);
                    else
                        this.writeSectionIncrementClockTicksForTID(taskIdx);
                    end
                end
                
                if ~this.codeInfoUtils.isMultiTasking
                    % singletasking
                    if this.AbsTimeData.XILWrapperProvideClockTicks &&...
                            ~this.codeInfoUtils.isExportFunctions
                        % export function doesn't need have separate sub-rates
                        this.writeSectionIncrementClockTicksForSingleTaskingSubRates();
                    end
                    
                    if this.codeInfoUtils.isTaskCounterRequired
                        % singletasking rate scheduler is called after output &
                        % update
                        if taskIdx == 1
                            this.writer.newLine;
                            this.writer.writeLine('/* call scheduler */');
                            this.writer.writeLine('rate_scheduler();');
                        end
                    end
                end
            end
        end
        
        function writeSectionUpdateFunctionCall(this, taskIdx, outputTask)
            % call super class method
            writeSectionUpdateFunctionCall@rtw.pil.TargetInterface(this, taskIdx, outputTask);
            
            % increment clockTick for this task            
            if this.AbsTimeData.XILWrapperProvideClockTicks
                this.writeSectionIncrementClockTicksForTID(taskIdx);
            end
            
            if ~this.codeInfoUtils.isMultiTasking
                % singletasking
                if this.AbsTimeData.XILWrapperProvideClockTicks
                    this.writeSectionIncrementClockTicksForSingleTaskingSubRates();
                end
                
                if this.codeInfoUtils.isTaskCounterRequired
                    % singletasking rate scheduler is called after update
                    % function
                    if taskIdx == 1
                        this.writer.writeLine('/* call scheduler */');
                        this.writer.writeLine('rate_scheduler();');
                    end
                end
            end
        end                                
        
        function writeSectionEnableBody(this)
            % write the Enable functions for both the model enable function
            % and export functions enable functions
            enableFunctions = this.codeInfo.EnableFunction;
            if isempty(enableFunctions)
                % call super class
                writeSectionEnableBody@rtw.pil.TargetInterface(this);      
            else
                isEnable = true;
                this.writeSectionEnableDisableBody(isEnable);
            end
        end
        
        function writeSectionDisableBody(this)
            % write the Enable functions for both the model disable
            % function and export functions disable functions
            disableFunctions = this.codeInfo.DisableFunction;
            if isempty(disableFunctions)
                % call super class
                writeSectionDisableBody@rtw.pil.TargetInterface(this);                
            else
                isEnable = false;
                this.writeSectionEnableDisableBody(isEnable);
            end
        end
        
        function writeSectionEnableDisableBody(this, isEnable)
            if isEnable
                enableDisableFunctions = this.codeInfo.EnableFunction;
            else
                enableDisableFunctions = this.codeInfo.DisableFunction;
            end
            this.writer.writeLine('switch(xilTID) {');
            % switch indent
            this.writer.incIndent;            
            expEnableDisableFcns =...
                this.getCodeInfoUtils.getAllExportFunctionsEnableDisableFcns(isEnable);
            
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;                

            for idx = 1:length(enableDisableFunctions)
                enableDisableFunction = enableDisableFunctions(idx);
                % make any required enum assignments
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.codeInfoUtils.getDataInterfacesForFunction(enableDisableFunction, 'input'), ...
                    'input');
                expFcnIdx = [];
                if ~isempty(expEnableDisableFcns)
                    [~,expFcnIdx] = find(strcmp(enableDisableFunction.Prototype.Name, {expEnableDisableFcns.fcn}));
                end
                isModelEnableDisableFunction = isempty(expFcnIdx);
                if isModelEnableDisableFunction
                    % model enable/disable function always uses id 0
                    id = 0;
                else
                    id = expEnableDisableFcns(expFcnIdx).outputFcn.id;
                end
                this.writer.writeLine(['case ' int2str(id) ':']);
                this.writer.incIndent;
                
                this.writeInputAssignments(dataElements);
                % params
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.getParamIndicesForFunction(enableDisableFunction), ...
                    'parameter');
                this.writeInputAssignments(dataElements);

                if isExecutionProfilingEnabled
                    [sectionIdForFunction, instrCalls] =...
                        this.getProfilingIdentifier(enableDisableFunction);
                    
                    this.writer.writeLine('%s(%dU);', ...
                                          instrCalls{1}, ...
                                          sectionIdForFunction);  
                end

                % call function
                this.writer.writeLine([enableDisableFunction.getFunctionCall ';']);

                if isExecutionProfilingEnabled
                    this.writer.writeLine('%s(%dU);',...
                                          instrCalls{2},...
                                          sectionIdForFunction);
                end
                
                % make any required enum assignments
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.codeInfoUtils.getDataInterfacesForFunction(enableDisableFunction, 'output'), ...
                    'output');
                
                % we write output assignment for all disable functions and
                % model enable function
                if ~isEnable || (isEnable && isModelEnableDisableFunction)
                    this.writeOutputAssignments(dataElements);
                end
                this.writer.writeLine('break;');
                this.writer.decIndent;
            end
            % generate default
            this.writer.writeCellLines({'default:';
                '   return XIL_INTERFACE_UNKNOWN_TID;'});
            % switch indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
        end
               
        function [mdlrefTIDs, mdlrefTIDValues] = getMdlRefTIDData(this)
            % returns "mdlref_TIDX" internal data entries that are required
            % for initialization of models using the timingBridge
            mdlrefTIDValues = {};
            data = this.codeInfo.InternalData;
            keepIndices = zeros(1, length(data));
            for i=1:length(data)
                currData = data(i);
                currIdentifier = ...
                    rtw.connectivity.CodeInfoUtils.getIdentifier(currData);
                tid = regexp(currIdentifier, ...
                             '^mdlref_TID(\d+)$', 'tokens', 'once'); 
                if ~isempty(tid)
                    keepIndices(i) = 1;
                    mdlrefTIDValues{end+1} = tid{1}; %#ok<AGROW>
                end                
            end
            mdlrefTIDs = data(logical(keepIndices));
        end
        
        function validateInterface(this)
            % call super class method            
            validateInterface@rtw.pil.TargetInterface(this);            
            
            % Model block SIL/PIL does not supports absolute time if the
            % referenced model contains any asynchronous rates. This
            % limitation only affects the older modeling style using task
            % specification blocks. The new modeling style using exported
            % function models anyways does not allow absolute time blocks
            % unless the trigger port has the sample time set to periodic
            % in which case the generated code maintains the clockTicks, so
            % model block SIL/PIL works fine with that. Note that 
            % XILWrapperProvideClockTicks is already set correctly for the
            % exported function models case.
            absTimeData = this.getAbsoluteTimeData;
            if absTimeData.XILWrapperProvideClockTicks &&...
                    ~isempty(this.codeInfoUtils.getRates('ASYNCHRONOUS'))
                rtw.pil.ProductInfo.error('pil', 'ModelBlockTaskSpecUsesAbsTimeNotSupported', ...
                    this.codeInfo.GraphicalPath);
            end
            
            % process errorStatus
            this.errorStatusData = this.getErrorStatusData;
            if ~isempty(this.errorStatusData)
                % check error status needs to be defined
                assert(~this.errorStatusData.Implementation.isDefined, ...
                       'Error status is already defined');
                this.errorStatusRequired = true;
            else
                this.errorStatusRequired = false;
            end
            % process stopRequested
            this.stopRequestedData = this.getStopRequested;
            if ~isempty(this.stopRequestedData)
                % check stopRequested needs to be defined
                assert(~this.stopRequestedData.Implementation.isDefined, ...
                       'stopRequested is already defined');
                this.stopRequestedRequired = true;
            else
                this.stopRequestedRequired = false;
            end
        end
        
        function validateParamDataInterface(this, dataInterface)            
            % ModelArgs and WorkspaceVariable params are fully
            % supported, although the user is responsible for defining
            % undefined parameters that can't be auto-defined such as
            % Const, ConstVolatile, otherwise link errors will occur.
            %
            % Const, ConstVolatile can be auto-defined once a suitable
            % initial value becomes available in CodeInfo.
            if ~this.codeInfoUtils.isModelArgParam(dataInterface) && ...
               ~this.codeInfoUtils.isWorkspaceParam(dataInterface)               
                % This case corresponds to a block dialog parameter in All
                % Tunable mode.
                %
                % We expect it to be defined so the code
                % will be initialized correctly.    
                %
                % However, we have no support for tuning it and we do not
                % warn about unsupported block dialog parameters.
                if ~(this.SILPILWrapperUtils.isCPPEncap || ...
                            ... % skip this assert for C++ class generation because
                            ... % there are cases where Implementation is empty and
                            ... % cases where isDefined returns false - these 
                            ... % parameters are always defined for C++ class
                            ... % generation.  See also g1129309.
                        this.getCodeInfoUtils.isLUTorBPwithDataFromInport(dataInterface))
                            ... % skip this assert for LookupTables or Breakpoint interfaces
                            ... % that take their data from a block inport - these 
                            ... % interfaces correspond to what is connected to the 
                            ... % block inport and may not be defined at this stage.
                    assert(logical(this.isDefined(dataInterface.Implementation)), ...
                        'Tunable non-model arg / non-workspace param is not defined in model reference component.');
                end
            end                                                 
        end
        
        function isCAPIEnabled = isCAPIEnabled(this)
            if strcmp(this.SILPILWrapperUtils.getParam('RTWCAPIParams'), 'on') || ...
                  strcmp(this.SILPILWrapperUtils.getParam('RTWCAPIRootIO'), 'on') || ...
                  strcmp(this.SILPILWrapperUtils.getParam('RTWCAPIStates'), 'on') || ...
                  strcmp(this.SILPILWrapperUtils.getParam('RTWCAPISignals'), 'on')
                isCAPIEnabled = true;
            else
                isCAPIEnabled = false; 
            end            
        end       
    end    
    
    methods (Access = 'protected')
        function absTimeData = getAbsoluteTimeData(this)
            modelInterface = this.SILPILWrapperUtils.getInfoStruct.modelInterface;
            
            assert(all(modelInterface.ClockTicksReq >= modelInterface.LongClockTicksReq), ...
                'clockTicks should always be required when clockTicksH is required');
            
            % Absolute time information can only be passed down to the
            % referenced model(s) through the TimingBridge. 
            timingBridgeRequired = this.codeInfoUtils.getTimingBridgeInfo;            
            absTimeData.XILWrapperProvideClockTicks = timingBridgeRequired &&...
                modelInterface.NeedAbsoluteTime;            
            
            absTimeData.ClockTicksReqVector = modelInterface.ClockTicksReq;
            absTimeData.LongClockTicksReqVector = modelInterface.LongClockTicksReq;
            
            absTimeData.isClockTicksRequired = any(absTimeData.ClockTicksReqVector);
            absTimeData.isLongClockTicksRequired = any(absTimeData.LongClockTicksReqVector);
        end  
    end
        
    methods (Access = 'private')  
        function errorStatus = getErrorStatusData(this)
            errorStatus = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_errorStatus');
        end
        
        function tid = getTIDData(this)
            tid = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'tid');
        end
        
        function stopRequested = getStopRequested(this)
            stopRequested = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_stopRequested');
        end      
        
        function parentMMI = getParentMMI(this)
            parentMMI = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_ParentMMI');
        end
        
        function childPath = getChildPath(this)
            childPath = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_ChildPath');
        end
        
        function childMMIIdx = getChildMMIIdx(this)
            childMMIIdx = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_ChildMMIIdx');
        end
        
        function cStateIdx = getCStateIdx(this)
            cStateIdx = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'rt_CSTATEIdx');
        end                
        
        function rtModel = getRTModel(this)
            rtModel = this.codeInfoUtils.getDataFromIdentifier(this.codeInfo.InternalData, ...
                'RTModel');
        end
        
        function writeSectionSystemInitOrResetFunction(this, fun)     
            if isempty(fun)
                this.writeEmptyFunctionCall;
            else                
                % make any required assignments
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.codeInfoUtils.getDataInterfacesForFunction(fun, 'input'), 'input');
                this.writeInputAssignments(dataElements);
                % params
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.getParamIndicesForFunction(fun), 'parameter');
                this.writeInputAssignments(dataElements);
                
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                if isExecutionProfilingEnabled
                    
                    [sectionIdForFunction, instrCalls] =...
                        this.getProfilingIdentifier(fun);
                    
                    this.writer.writeLine('%s(%dU);', ...
                        instrCalls{1}, ...
                        sectionIdForFunction);
                end
                
                % call the function
                this.writer.writeLine([fun.getFunctionCall ';']);
                
                if isExecutionProfilingEnabled
                    this.writer.writeLine('%s(%dU);',...
                        instrCalls{2},...
                        sectionIdForFunction);
                end
                
                % make any required assignments
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.codeInfoUtils.getDataInterfacesForFunction(fun, 'output'), 'output');
                this.writeOutputAssignments(dataElements);
                % no cases where constant outputs may have been updated; no
                % need to update I/O data
                assert(~this.codeInfoUtils.hasConstOutput, ...
                    'No constant outputs expected when code interface contains InitializeConditions');
            end
        end
    end
end
