classdef (Hidden = true) TargetInterface < rtw.connectivity.CodeInfoWriterProject
%TARGETINTERFACE creates a target interface file defined by CodeInfo
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2008-2024 The MathWorks, Inc.

    % properties
    properties (SetAccess = 'private', GetAccess = 'protected')
        SILPILWrapperUtils;     
        InSwitchStatement;
    end
    
    properties (SetAccess = 'private', GetAccess = 'private')
        DefinedDataList;
        InitializeDataFcns = '';
        FreeDataFcns = '';
        AccessFcnsData = {};
        HasModelWideEvents = false;
        SelfCoderDataGroup;
        AllocatesCppObject = false;
        IsImportedDialogParam = [];
        TransferInterfaceUtils;
        DynamicallyAssignedPointersList = {};
        ServicePreInitPrototype;
        ServicePreInitFunctionCall;
        MessageInterfaceClassMap; % TODO g2923515: Remove with new CodeDesc class
    end
    
    properties (Access = protected)
        InterfaceStrategy coder.internal.connectivity.TargetInterfaceStrategy
    end
    
    methods
        % constructor
        function this = TargetInterface(silPILWrapperUtils, ...
                                        targetInterfacePath, ...
                                        options)
            arguments
                silPILWrapperUtils (1,1) rtw.pil.SILPILWrapperUtils
                targetInterfacePath
                options.InterfaceStrategy (1,1) ...
                    coder.internal.connectivity.TargetInterfaceStrategy = ...
                    coder.internal.connectivity.TargetInterfaceStrategy(silPILWrapperUtils);
            end
            
            % validate arg            
            rtw.connectivity.Utils.validateArg(silPILWrapperUtils, ...
                'rtw.pil.SILPILWrapperUtils');
            
            % call super class constructor                             
            this@rtw.connectivity.CodeInfoWriterProject(silPILWrapperUtils.getCodeInfoUtils, ...
                                                        targetInterfacePath);              
            % store SILPILWrapperUtils
            this.SILPILWrapperUtils = silPILWrapperUtils;
            this.InterfaceStrategy = options.InterfaceStrategy;
            
            % validate interface
            this.validateInterface;
            
            % Set model wide events
            this.HasModelWideEvents = this.getCodeInfoUtils.hasModelWideEvents();
            
            this.TransferInterfaceUtils = coder.internal.connectivity.TransferInterfaceUtils(false);

            % initialize service pre-init info
            [this.ServicePreInitPrototype, this.ServicePreInitFunctionCall] = ...
                    this.SILPILWrapperUtils.getClientInterface.processServicePreInit(this.codeInfo);

            % TODO g2923515: The following map will no longer be required once the
            % new codeDesc message service class is available, as type
            % replacement on the constructor args will be available via the
            % opaque region
            pluginManager = this.SILPILWrapperUtils.getSILPILInterface().getServicePluginManager();
            if matlab.internal.feature("XILMessageSupport")
                if ~isempty(pluginManager) && pluginManager.HasActiveServices
                    this.MessageInterfaceClassMap = containers.Map;
                    modelName = this.codeInfo.Name;
                    inports = this.codeInfo.Inports;
                    outports = this.codeInfo.Outports;
                    ports = [inports outports];
                    messageInterfaceClasses = {};
                    for i = 1:length(ports)
                        if isa(ports(i), 'coder.descriptor.MessageDataInterface')
                            messageInterfaceClasses{end+1} =  ports(i).Type.BaseType.Identifier; %#ok<AGROW>
                        end
                    end
                    messageInterfaceClasses = unique(messageInterfaceClasses);
                    for i = 1:length(messageInterfaceClasses)
                        this.MessageInterfaceClassMap(messageInterfaceClasses{i}) = ...
                            [modelName messageInterfaceClasses{i}];
                    end
                end
            end
        end
        
        function set.InterfaceStrategy(this, strategy)
            % set and initialize the strategy
            this.InterfaceStrategy = strategy;
            this.InterfaceStrategy.initialize(this);
        end

        % get an expression for a DataElement of an Implementation
        %
        % index argument is only used for vector or array expressions
        function expression = getElementExpression(this, ...
                                                   implementation, ...
                                                   cData, ...
                                                   index)            
            % get DataElement sub-expression
            isPointerTypeSupported = true;
            isLeafPointerAllowed = true;
            subExpression = cData.getElementExpression(index, isPointerTypeSupported, isLeafPointerAllowed);
            % see if subExpression starts with an index
            if ~isempty(subExpression) && (subExpression(1) == '[')        
                subExpressionStartsWithIndex = true;                
            else
                subExpressionStartsWithIndex = false; 
            end
            
            switch this.codeInfoUtils.class(implementation)                                    
                case 'ArrayExpression'                    
                    % get the base region in order to handle indexing 
                    % correctly
                    baseRegion = implementation.BaseRegion;
                    targetIdentifier = this.getExpression(baseRegion);                                         
                    expression = [targetIdentifier '[' int2str(implementation.Offset) ']'];
                    % if subExpression starts with an index then we need
                    % to switch to pointer arithmetic
                    if subExpressionStartsWithIndex                    
                        expression = ['(&' expression ')'];                    
                    end                                        
                case 'PointerVariable'                
                    assert(logical(implementation.Type.isPointer), 'PointerVariable must have pointer type.');
                    expression = this.getExpression(implementation);                                        
                    % if subExpression does not start with an index then we
                    % need to dereference the scalar pointer!                    
                    if ~subExpressionStartsWithIndex
                        expression = ['(*' expression ')'];
                    end                    
                case 'TypedCollection'
                    dataElementList = cData.getDataInterface.getTargetDataElements;
                    %Only ever going to be 2 storage implementations from a
                    %a typed collection. Last one are always size as will
                    %the last data element associated with that interface.
                    if cData==dataElementList(end)
                        elementIndex = 2;
                    else
                        elementIndex = 1;
                    end
                    elements = this.codeInfoUtils.getArray(implementation.Elements);
                    expression = this.getExpression(elements(elementIndex));
                case 'BasicAccessFunctionExpressionCollection'
                    % In BasicAccessFunctionExpressionCollection the 
                    % expression is the same for both elements, so
                    % process only the first one
                    implementation = implementation.Elements(1);
                    expression = this.getExpression(implementation);
                case 'AutosarCalibration'     
                    baseRegion = this.getCodeInfoUtils.getAutosarCalibrationBaseRegion(implementation);
                    if ~isempty(baseRegion)
                        % for subcomponent/model reference simulations, 
                        % get the expression of the BaseRegion, e.g. the
                        % struct containing the parameters
                        expression = this.getExpression(baseRegion);
                    else
                        % for component simulations, no implementation in
                        % this case, getExpression returns the global data
                        % defined in the stub e.g. Rte_CData_*_data
                        expression = this.getExpression(implementation);
                    end
                otherwise                    
                    expression = this.getExpression(implementation);
            end                                                            
            % add subExpression
            expression = [expression subExpression];            
        end
        
        function data = getAccessFunctionsData(this)
            data = this.AccessFcnsData;
        end
        
                
        function expression = getExpression(this, implementation)
            expression = this.InterfaceStrategy.getExpression(implementation);
        end
        
        function isDefined = isKeyInDefinedDataList(this, expression)
             isDefined = this.DefinedDataList.isKey(expression);
        end
    end        
    
    methods (Access = 'public', Static)
        function isArray = isArrayInCode(argInfoOrEmbTypeObj, dimensions)
            isArray = (dimensions > 1) || rtw.pil.TargetInterface.isArrSizeOne(argInfoOrEmbTypeObj);
        end
        
        function isArrSzeOne = isArrSizeOne(argInfoOrEmbTypeObj)
            if (isstruct(argInfoOrEmbTypeObj))
                embT = argInfoOrEmbTypeObj.Implementation.Type;
            else
                embT = argInfoOrEmbTypeObj;
            end
            
            assert(rtw.connectivity.CodeInfoTypeUtils.isaType(embT,'Type'));
            isArrSzeOne = embT.isMatrix && embT.ArrSizeOne;
        end
    end
        
    methods (Access = 'protected')
        function checkIOReadOnly(this, dataInterface, dataInterfaceType)
            % check data interfaces are not read-only. This method can be
            % overridden by child classes.
            %
            % e.g.
            % - In-the-Loop must be able to write to Inports
            % - model.c must be able to write to Outports (compilation should
            %   actually have failed before this check runs)

            arguments
                this
                dataInterface coder.descriptor.DataInterface {mustBeScalarOrEmpty}
                dataInterfaceType char {mustBeMember(dataInterfaceType, {'inport', 'outport', 'datastore'})}
            end

            isReadOnly = this.codeInfoUtils.isDataInterfaceReadOnly(dataInterface);
            if isReadOnly
                diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, dataInterfaceType);
                this.throwReadOnlyDiagnostic(diString);
            end
        end

        function throwReadOnlyDiagnostic(~, dataInterfaceString)
            % Throw read only error. This method can be overridden by
            % child classes.
            diagObj = MSLException(message('PIL:pil:ReadOnlyDataInterface', dataInterfaceString));
            diagObj.throw();
        end

        function writeOutputBody(this)
            this.writeSectionHeader;
            this.writeSectionIncludes;
            this.writeSectionDefines;
            this.writeSectionStorage;
            this.writeSectionStorageForAssignmentData;
            this.writeSectionXILIOData;   
            this.writeSectionEntryPointDeclarations();
            this.writeSectionGetSetFunctions;
            this.writeSectionDynamicAccessFunctions;
            this.writeSectionUserFunctions;
            this.writeSectionProcessParams;
            this.writeSectionUploadProfilingDataPoint;
            this.writeSectionGetDataTypeInfo;
            this.writeSectionStartupSimulinkVariantFunctions;
            this.writeSectionCallerFunctions;
            this.writeSectionInit;
            this.writeSectionPause;
            this.writeSectionSystemInitialize;
            this.writeSectionSystemReset;
            this.writeSectionInitUDataProcessing;
            this.writeSectionInitSDataProcessing;
            this.writeSectionOutput;
            this.writeSectionUpdate;
            this.writeSectionInitYDataProcessing;
            this.writeSectionTerminate;
            this.writeSectionInitTargetData;
            this.writeSectionEnable;
            this.writeSectionDisable;
        end
        
        function writeSectionHeader(this)
            this.writer.writeCellLines({'/*';
                [' * File: ' this.SILPILWrapperUtils.getSILPILInterface.getWrapperSourceInfo];
                ' *';
                [' * ' this.SILPILWrapperUtils.getSILPILInterface.getSimModeAcronym ...
                ' generated interface for code: "' this.codeInfo.Name '"'];
                ' *';
                ' */'});
            this.writer.newLine;
        end

        function shouldReplace = shouldReplaceRTMPointerVar(~)
            % By default we don't replace the target region of RTM pointer
            shouldReplace = false;
        end

        function writeSectionIncludes(this)
            
            if ~isempty(this.SILPILWrapperUtils.getLoggingData)
                %Write out xil header file
                this.writer.wLine('#include <stddef.h>');
            end

            %Construct the list of model Header files based on header files
            %listed in CodeInfo
            %Get all functions from CodeInfo. This list needs maintaining
            %as codeInfo changes.
            allFcns = [this.codeInfo.InitializeFunctions, ...
                this.codeInfo.OutputFunctions, ...
                this.codeInfo.UpdateFunctions, ...
                this.codeInfo.TerminateFunctions];
            
            %Construct a full list of header files, then remove duplicates
            headerList = cell(numel(allFcns),1);
            for fcnIdx  = 1 : numel(allFcns)
                headerList{fcnIdx} = allFcns(fcnIdx).Prototype.HeaderFile;
            end
            
            % Explicitly add model.h as it may not be part of header files
            % listed in codeInfo (e.g. for AUTOSAR)
            modelH = this.codeInfoUtils.getModelHeader;
            
            % Imported Grouped storage classes might also have a header
            % associated
            lDIs = [this.codeInfo.Inports, ...
                this.codeInfo.Outports, ...
                this.codeInfo.DataStores, ...
                this.codeInfo.Parameters];
            lImportedGroupedHeaderFiles = this.getGroupedCSCHeaderFiles(lDIs);
            
            lPrivateHader = this.SILPILWrapperUtils.getClientInterface.getPrivateHeader(this.SILPILWrapperUtils);

            
            % Include service header files
            pluginManager = this.SILPILWrapperUtils.getSILPILInterface().getServicePluginManager();
            if ~isempty(pluginManager)
                serviceHeaders = pluginManager.getIncludeHeaders();
                headerList = [headerList; serviceHeaders'];
            end

            startupSimVarFcnHeaders = this.getStartupSimulinkVariantFcnHeaders();

            
            %Model.h should be included before SimulinkFunctions headers
            %because it includes the Variants definitions if any.
            headerList = unique([ ...
                modelH; ...
                headerList; ...
                lImportedGroupedHeaderFiles(:); ...
                lPrivateHader; ...
                startupSimVarFcnHeaders(:)], 'stable');
                                   
            %Write out individual header files.
            targetExtension = this.SILPILWrapperUtils.getSILPILInterface().getTargetExtension();
            for headerIdx = 1:numel(headerList)
                % For AUTOSAR target, the runnable declarations are not
                % generated and hence the HeaderFile would be empty.
                % Subsystem declarations may be registered in .c/.cpp
                % files, skip these includes.
                if ~isempty(headerList{headerIdx}) && ~endsWith(headerList{headerIdx}, targetExtension)
                    this.writer.wLine('#include "%s"',headerList{headerIdx});
                end
            end
            
            %Write out xil header file. Put this after any generated
            %application includes to avoid redefinitions
            this.writer.wLine('#include "xil_interface.h"');
            
            isCodeInstrumentationEnabled =...
                this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                this.SILPILWrapperUtils.isProfilingEnabled;
            
            if isCodeInstrumentationEnabled
                [~, ~, ~, hProfilingHeaderFull] = this.SILPILWrapperUtils.getSILPILInterface...
                    .getSourceFileProfiling;
                [~,f,e] = fileparts(hProfilingHeaderFull);
                hProfilingHeaderShort = [f e];
                
                this.writer.writeLine('#include "%s"', ...
                                      hProfilingHeaderShort);
            end    


            
            this.writer.writeCellLines...
                ({'/* Functions with a C call interface */';
                '#ifdef __cplusplus';
                'extern "C" {';
                '#endif';
                '#include "xil_data_stream.h"'});                 
            if isCodeInstrumentationEnabled
                this.writer.wLine('#include "codeinstr_data_stream.h"');
            end
            this.writer.writeCellLines...
                ({'#ifdef __cplusplus';
                '}';
                '#endif'});
            
            % check for and include unique header files for Get/Set
            % functions                        
            dataInterfaces = this.getDataInterfacesForGetSetFunctionProcessing;
            headerFiles = {};
            for dataInterfaceIdx = 1:length(dataInterfaces)
              dataInterface = dataInterfaces(dataInterfaceIdx);
              implementation = dataInterface.Implementation;
              headerFile = '';
              isGetSetFunctionAccess = rtw.pil.TargetInterface.isGetSetFunctionAccess(implementation);
              isBasicAccFcnExpression = rtw.connectivity.CodeInfoUtils.isa(implementation, 'BasicAccessFunctionExpression');

              if isGetSetFunctionAccess
                headerFile = implementation.HeaderFile;
              elseif isBasicAccFcnExpression
                headerFile = implementation.Prototype.HeaderFile;
              end
              if isGetSetFunctionAccess || isBasicAccFcnExpression
                if isempty(headerFile)
                  % get set belong to different interface types, i.e.
                  % inports, outports, parameters and datastore
                  interfaceType = 'unspecified';
                  diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, interfaceType);
                  DAStudio.error('PIL:pil:GetSetHeaderFileMissing', ...
                    diString);
                else
                  headerFiles{end+1} = headerFile; %#ok<AGROW>
                end
              end
            end
            % remove duplicates
            headerFiles = unique(headerFiles);
            % include
            if ~isempty(headerFiles)
                this.writer.writeLine('/* Get/Set function header file(s) */');      
                for headerFileIdx = 1:length(headerFiles)
                    headerFile = headerFiles{headerFileIdx};
                   this.writer.writeLine('#include "%s"', headerFile);
                end             
            end
            this.writer.newLine;    
            
            this.InterfaceStrategy.writeSectionIncludesHook(this.writer);

            % Declare service pre-initialize (no header) if required
            if ~isempty(this.ServicePreInitPrototype)
                this.writer.wLine('%s;', this.ServicePreInitPrototype);
                this.writer.newLine;
            end                
        end                
        
        function writeSectionDefines(this)             
            if this.SILPILWrapperUtils.isMatFileLoggingEnabled
                this.writeSectionDefinesMatFileLogging();
            end
        end
        
        function writeSectionStorage(this)  
            % reset list of defined data so we can track duplicates
            this.DefinedDataList = containers.Map.empty;                
                        
            % arguments of constructor function (C++ only)
            dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getClassConstructorActualArgs), 'constructorArg');
            this.writeSectionStorageForData(dataInterfaces);

            % signals
            %
            % need to define inactive data
            filterInactiveInterfaces = false;
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.InterfaceStrategy.getInportIndicesForStorage(), ...
                'input', filterInactiveInterfaces), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.InterfaceStrategy.getOutportIndicesForStorage(), ...
                'output', filterInactiveInterfaces)];
            
            % we need to allocate memory for IO ports even if they are not
            % active, because they may be mapped as arguments in an active
            % function interface (e.g. in model step)
            mustAllocate = true;
            this.writeSectionStorageForData(dataInterfaces, mustAllocate);            

            % all shared data stores
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                            rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite'), ...
                            rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getSharedLocalDataStores), 'sharedLocalDataStore')];            
            this.writeSectionStorageForData(dataInterfaces);
                                        
            % params                        
            dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                                this.getParamIndicesForStorage, 'parameter');
            this.writeSectionStorageForData(dataInterfaces);
            
            %Allocation Function Required Data
            if this.SILPILWrapperUtils.needStorageForAllocationErrorMessage()
                this.writer.wLine('const char *RT_MEMORY_ALLOCATION_ERROR = "memory allocation error";');
            end
            
            % Need to define storage for server functions input and output
            % args.
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo;
            dataInterfaces = rtw.pil.DataInterface.empty;
            for svrIdx = 1:length(serverFunctionsInfo)
                currServerFcn = serverFunctionsInfo(svrIdx);
                % Storage is not needed for:
                % - local server functions
                % - global functions that are not called from outside the component
                if currServerFcn.IsExported
                    dataInterfaces = [dataInterfaces serverFunctionsInfo(svrIdx).getAllCodeInfoDataInterfaces]; %#ok<AGROW>
                end
            end
            this.writeSectionStorageForData(dataInterfaces);
            
            % Need to define storage for caller functions input and output
            % args
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            dataInterfaces = rtw.pil.DataInterface.empty;
            for callIdx = 1:length(callerFunctionsInfo)
                dataInterfaces = [dataInterfaces callerFunctionsInfo(callIdx).getAllCodeInfoDataInterfaces]; %#ok<AGROW>
            end
            this.writeSectionStorageForData(dataInterfaces);

            % allocate storage for more data if necessary
            this.writeSectionStorageHook;
        end

        function writeSectionStorageInternalData(this, options)
            % Define variables for InternalData data interfaces
            arguments
                this
                options.UseCachedDataInterfaces = true;
            end

            [idxRange, rtmPointerImpl] = this.getInteralDataIdxForStorage( ...
                this.IsImportedDialogParam);
            dataInterfaces = this.getStorageInterfacesFromInternalData(idxRange, ...
                options.UseCachedDataInterfaces);
            this.writeSectionStorageForData(dataInterfaces);
            if ~isempty(rtmPointerImpl)
                % define RTM pointer, but not initialize it.
                this.writer.writeLine('%s * %s;', ...
                    rtmPointerImpl.Type.BaseType.Identifier, rtmPointerImpl.Identifier);
            end
        end
        
        function callerFunctionsInfo = getCallerFunctionsInfo(this)
            callerFunctionsInfo = this.InterfaceStrategy.getCallerFunctionsInfo();

        end

        function needsDeclaration = needExternDeclarationForCSC(this, implementation)
            % For exported CSC data, the DEFINITION is always generated
            % into the source file.  However, code placement options in the
            % configuration set allows the (extern) DECLARATION to either
            % be generated into a header file (and the header file
            % #included by any source files that use the data) or you can
            % put a separate extern declaration into each of the source
            % files. In the latter case, the SIL/PIL wrapper would need to
            % these CSCs as extern to avoid compilation errors.
            
            needsDeclaration = false; %assume            
            storageSpecifier = this.getCodeInfoUtils.getStorageSpecifier(...
                implementation);
            if strcmp(storageSpecifier, 'extern')
                baseImplementation = this.getBaseStorageImplementation(implementation);
                if isempty(baseImplementation.DeclarationFile) &&...
                        ~isempty(baseImplementation.DefinitionFile)
                    needsDeclaration = true;
                end
            end
        end

        function writeSectionStorageForData(this, xilDataInterfaces, varargin)
            assert(isa(xilDataInterfaces, 'rtw.pil.DataInterface'), ...
                'xilDataInterfaces must be an rtw.pil.DataInterface');
            % parse varargin
            p = inputParser;
            p.addOptional('MustAllocate', false, ...
                @(x)validateattributes(x,{'logical'},{'scalar'}));
            p.parse(varargin{:});
            mustAllocate = p.Results.MustAllocate;
            
            for i=1:length(xilDataInterfaces)
                xilDataInterface = xilDataInterfaces(i);
                dataInterface = xilDataInterface.getRTWDataInterface;
                % if the Data belongs to an active variant or if we need to
                % allocate it anyway
                if mustAllocate || (rtw.pil.CodeInfoData.isInterfaceActive(dataInterface) &&...
                        rtw.connectivity.CodeInfoUtils.isDataInterfaceAccessible(dataInterface))
                    
                    implementationList = this.codeInfoUtils.getImplElementsIfCollection(dataInterface.Implementation);
                    for idx = 1:length(implementationList)
                        implementation = implementationList(idx);
                        if ~this.isDefined(implementation) && ...
                                ~this.codeInfoUtils.isImportedGroupedCSC(implementation) && ...
                                ~this.codeInfoUtils.isBasicAccessFunctionExpressionForIO(xilDataInterface, implementation) && ... 
                                ~this.isGetSetFunctionAccessWithOpaqueRegion(implementation)
                            this.writeInterfaceData(xilDataInterface, implementation);
                        elseif this.codeInfoUtils.isa(implementation,'ClassMethodExpression') && ~this.isGetSetMemoryDefined(implementation)
                            this.writeInterfaceData(xilDataInterface, implementation);
                        else
                            if this.needExternDeclaration(implementation)
                                this.writeDeclarationDataForBaseStorage(xilDataInterface, implementation);
                            end
                        end

                        if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation)
                            %Opaque Region Initialize Functions include newline
                            if ~isempty(implementation.OpaqueRegion.InitializeDataFunction.Code)
                                % guard against empty Code as the
                                % getFunctionCall method returns just the
                                % argument in that case
                                lRawMethod = implementation.OpaqueRegion.InitializeDataFunction.getFunctionCall(dataInterface);
                                lMethod = this.processOpaqueRegionMethod(lRawMethod, implementation);
                                this.InitializeDataFcns = [this.InitializeDataFcns lMethod];
                            end
                            if ~isempty(implementation.OpaqueRegion.FreeDataFunction.Code)
                                % guard against empty Code as the
                                % getFunctionCall method returns just the
                                % argument in that case
                                lRawMethod = implementation.OpaqueRegion.FreeDataFunction.getFunctionCall(dataInterface);
                                lMethod = this.processOpaqueRegionMethod(lRawMethod, implementation);
                                this.FreeDataFcns = [this.FreeDataFcns lMethod];
                            end
                        end

                        pointerExpression = rtw.connectivity.CodeInfoUtils.getNestedImplementation(implementation, 'PointerExpression');
                        if isempty(this.codeInfo.AllocationFunction) && ~isempty(pointerExpression) && ...
                            ~pointerExpression.TargetRegion.isDefined

                            assert(this.codeInfoUtils.isa(pointerExpression.TargetRegion, 'Variable'), ...
                                'TargetRegion is not a Variable');
                            this.writeInterfaceData(xilDataInterface, pointerExpression.TargetRegion);

                        end
                        
                        if xilDataInterface.NeedsIOTargetTransform
                            if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation)
                                continue; %Skip processing for items with Opaque Regions
                            elseif ~isempty(implementation.DynamicMemory)
                                % get the base storage implementation (e.g. base struct storage,
                                % base array expression storage, target variable, etc)
                                storageImplementation = this.getBaseStorageImplementation(implementation);
                                this.validateBaseStorageImplementation(implementation, storageImplementation);
                                %Define additional extra variables located in
                                %the dynamic region for the interface.
                                staticVarList = implementation.DynamicMemory.StaticVariables;
                                if ~isempty(staticVarList)
                                    elements = this.codeInfoUtils.getArray(staticVarList.Elements);
                                    for eleIdx = 1:numel(elements)
                                        if ~this.isDefined(elements(eleIdx))
                                            this.writeInterfaceData(xilDataInterface, elements(eleIdx));
                                            this.setImplementationOwner(elements(eleIdx), 'PIL');
                                        end
                                    end
                                else
                                    staticVar = implementation.DynamicMemory.StaticVariable;
                                    if ~isempty(staticVar) && ~this.isDefined(staticVar)
                                        this.writeInterfaceData(xilDataInterface, staticVar);
                                        this.setImplementationOwner(staticVar, 'PIL');
                                    end
                                end
                                sizeVar = implementation.DynamicMemory.SizeVariable;
                                if ~isempty(sizeVar) && ~this.isDefined(sizeVar)
                                    this.writeInterfaceData(xilDataInterface, sizeVar);
                                end
                                indexVar = implementation.DynamicMemory.IndexVariable;
                                if ~isempty(indexVar) && ~this.isDefined(indexVar)
                                    this.writeInterfaceData(xilDataInterface, indexVar);
                                end
                            end
                        end
                    end
                end
            end
            
            % Handle any grouped CSCs
            if mustAllocate
                this.writeGroupedInterfaceData(xilDataInterfaces);
            end
        end
          
        function writeDeclarationDataForBaseStorage(this, xilDataInterface, implementation)
            assert(isa(xilDataInterface, 'rtw.pil.DataInterface'), ...
                'xilDataInterface must be an rtw.pil.DataInterface');
            % get the base storage implementation (e.g. base struct storage,
            % base array expression storage, target variable, etc)
            storageImplementation = this.getBaseStorageImplementation(implementation);
            this.validateBaseStorageImplementation(implementation, storageImplementation);
            declareExpression = this.getDeclareExpression(storageImplementation);
            this.writer.writeLine('%s', declareExpression);
            
            % add newline
            this.writer.newLine;
        end
        
        function writeInterfaceData(this, xilDataInterface, implementation)            
            assert(isa(xilDataInterface, 'rtw.pil.DataInterface'), ...
                'xilDataInterface must be an rtw.pil.DataInterface');   
            dataInterface = xilDataInterface.getRTWDataInterface;
            % get the base storage implementation (e.g. base struct storage,
            % base array expression storage, target variable, etc)
            storageImplementation = this.getBaseStorageImplementation(implementation);
            this.validateBaseStorageImplementation(implementation, storageImplementation);
            
            % add identifier prefix if applicable
            this.InterfaceStrategy.addStaticDataPrefix(dataInterface, storageImplementation);                             
            
            needsReplaceType = true;
            isPointerToMatrix = this.getIsPointerToMatrix(storageImplementation, dataInterface);
            
            % One case where the Implementation can be undefined and
            % the storageImplementation can be defined is:
            % (With reusable code) InternalData(i) corresponding to Parameter  
            % is a PointerExpression and InternalData(i+idx) 
            % corresponding to RTM is a PointerVariable. At "i" 
            % Implementation.BaseRegion.TargetVariable gets defined.
            % At "i+idx", Implementation is undefined but 
            % Implementation.TargetVariable is already defined.
            isBaseStorageDefined = this.isDefined(storageImplementation);
            isConstructorArg = rtw.pil.TargetInterface.getIsInterfaceConstructorArg(xilDataInterface);
            if (~isBaseStorageDefined)
                % In-the-Loop owner
                this.setImplementationOwner(storageImplementation, ...
                    'PIL');
                if isConstructorArg
                    % Set the model obj owner without defining the object access function.
                    % This allows calling getExpression on StructAccessorVariables to define the constructor arguments before defining obj access function.
                    modelObjectImplementation = this.getCPPModelObjectImpl();
                    this.setImplementationOwner(modelObjectImplementation, 'PIL');
                    % Reset the owner to empty once the argument has been defined so that the model object can be subsequently defined.
                    restoreOwner = onCleanup(@() this.setImplementationOwner(modelObjectImplementation, ''));
                else
                    this.writeCPPModelObjForIOAccess(storageImplementation);
                end
            end
            isPointerVariable = this.codeInfoUtils.isa(implementation, 'PointerVariable');
            definedData = {};
            definedExpressions = {};
                        
            if this.isCPPModelObject(storageImplementation)
                storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, xilDataInterface.DimsPreserved);
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                storageSpecifier = this.getStorageSpecOrMemSectionForXILDefinition(dataInterface);
                
                assert(~isPointerVariable, 'CPP Model Object was a pointer');
                assert(isempty(storageSpecifier), 'CPP Model Object has storage specifier.');
                assert(isempty(storageVolatile), 'CPP Model Object is volatile.');
                assert(isempty(storage_size_str), 'CPP Model Object is non-scalar.');
                
                %Write out model object
                dataExpression = this.getExpression(storageImplementation);
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerToMatrix);
                this.writeModelObjectAccessFunction(dataExpression, typeIdentifier);                
                
                definedData{end+1} = dataExpression;
                definedExpressions{end+1} = {};
            else 
                if this.dataNeedsGetSetAccessMethod(storageImplementation) || this.needToAllocate(dataInterface, isBaseStorageDefined, isPointerVariable)
                    % define the storage implementation
                    [definedData, definedExpressions] = this.writeInterfaceDataExpression(definedData, definedExpressions, xilDataInterface, implementation);
                end
                
                % define a pointer variable (statically initialized to the
                % storage implementation) if the current implementation is
                % a PointerVariable or contains one
                definedPrtVar = this.writeInterfaceDataForPointerVariable( ...
                    dataInterface, implementation, storageImplementation, ...
                    xilDataInterface.DimsPreserved, isPointerToMatrix);
                if ~isempty(definedPrtVar)                
                    definedData{end+1} = definedPrtVar;
                    definedExpressions{end+1} = {};                 
                end
            end            
            % check for duplicate definitions
            %
            % make sure we captured the defined data for all code paths
            % above
            assert(~isempty(definedData), ...
                'Some data must be defined.');
            assert(~any(this.isKeyInDefinedDataList(definedData)), ...
                'Duplicate data defined: %s', char(definedData));
            for idx = 1:numel(definedData)
                this.DefinedDataList(definedData{idx}) = definedExpressions{idx};
            end
        end

        function needGetSetAccessMethod = dataNeedsGetSetAccessMethod(this, storageImplementation)
            needGetSetAccessMethod = ...
                    this.codeInfoUtils.isa(storageImplementation, 'ClassMethodExpression') || ...
                    this.codeInfoUtils.isa(storageImplementation, 'StructAccessorVariable');
        end

        function [definedData, definedExpressions] = writeInterfaceDataExpression(this, definedData, definedExpressions, xilDataInterface, implementation)
            pointerVarImpl = rtw.connectivity.CodeInfoUtils.getNestedImplementation(implementation, 'PointerVariable');
            dataInterface = xilDataInterface.getRTWDataInterface;
            if(~isempty(pointerVarImpl) && this.shouldUseMdlRefDWorkRtmAsTargetVar(dataInterface))
                % The RTM pointer variable points an incorrect target region.
                % Skip the definition of this target region as it will result
                % in an unused variable.
                % TODO: remove this workaround when g2933332 is addressed.
                return;
            end
            storageImplementation = this.getBaseStorageImplementation(implementation);
            fullExpression = this.getDefineExpression(storageImplementation, xilDataInterface);

            if ~(this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression') && xilDataInterface.NeedsIOTargetTransform)
                this.writer.writeLine('/* interface data */');
                this.writer.writeLine(fullExpression);
            end

            if (this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression') && ~xilDataInterface.NeedsIOTargetTransform)
                this.AccessFcnsData{end+1} = fullExpression;
            end


            % add newline
            this.writer.newLine;

            dataExpression = this.getExpression(storageImplementation);
            definedData{end+1} = dataExpression;

            if this.dataNeedsGetSetAccessMethod(storageImplementation)
                % For Get/Set Method access, cache away get/set
                % call lines whilst creating interface data
                definedExpressions{end+1} = this.writeGetSetAccessMethod( ...
                    dataInterface, storageImplementation, this.getIsPointerToMatrix(storageImplementation, dataInterface), dataExpression);
            else
                %Otherwise nothing to add
                definedExpressions{end+1} = {};
            end
        end
        
        function writeGroupedInterfaceData(this, xilDataInterfaces)
            % Write definitions for imported grouped CSCs
            lRTWDIs = [];
            for i = 1:numel(xilDataInterfaces)
                lRTWDIs = [lRTWDIs, xilDataInterfaces(i).getRTWDataInterface];%#ok
            end
            lImplementations = this.getUniqueRTWDataImplementationWithGroupedCSC(lRTWDIs);
            arrayfun( @(x)this.writer.wLine('%s', this.getGroupedDefineExpression(x)), ...
                    lImplementations);
        end
        
        function indices = getParamIndicesForStorage(this)
            indices = [this.codeInfoUtils.getModelArgParams ...
                       this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams];
        end 
        
        function writeSectionStorageForAssignmentData(this)
            % list of data interfaces to process assignment storage requirements for                                                                                             
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo;            
            serverFunctionsDataElements = [];
            for svrIdx = 1:length(serverFunctionsInfo)
                serverFunctionsDataElements = [serverFunctionsDataElements serverFunctionsInfo(svrIdx).getAllDataElements]; %#ok<AGROW>
            end
            
            callerFunctionsInfo = this.getCallerFunctionsInfo;                     
            callerFunctionsDataElements = [];
            for svrIdx = 1:length(callerFunctionsInfo)
                callerFunctionsDataElements = [callerFunctionsDataElements callerFunctionsInfo(svrIdx).getAllDataElements]; %#ok<AGROW>
            end
            
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfo.Inports), 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfo.Outports), 'output'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.getParamIndicesForStorage, 'parameter'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite'),...
                                serverFunctionsDataElements,...
                                callerFunctionsDataElements];
            % process all logging data
            loggingData = this.SILPILWrapperUtils.getLoggingData;                       
            loggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(loggingData);
            for i=1:length(loggingData)
                dataElements = [dataElements loggingData(i).getTargetDataElements]; %#ok<AGROW>
            end
                            
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);                                
                if ~cData.isGetSetViaPointer
                    % get assignment data info
                    [dataType, identifier] = cData.getDataInterface.getAssignmentDataInfo(cData);
                    this.writeStorageDefinition(cData, dataType, identifier);
                end
            end
            
            % for each signal/state with CSC Custom Expression to log,
            % generate internal variables to store their values and 
            % register their Getters to be called and update their 
            % variables
            for logData = loggingData
                impl = logData.getRTWDataInterfaceImplementation();
                if this.needExternDeclarationForCSC(impl)
                    this.writeDeclarationDataForBaseStorage(logData, impl);
                elseif this.isGetSetCustomExpression(impl)
                    cData = logData.getTargetDataElements();
                    if isempty(cData) || ~cData.isGetSetViaPointer
                        continue;
                    end
                    rtw.pil.OwnerAccessor.setOwner(impl, 'PIL');
                    variableName = this.getExpression(impl);
                    if isempty(impl.ReadExpression)
                        continue;
                    end
                    % generate internal variable
                    dtypeId = cData.getDtypeId;
                    dataType = rtw.connectivity.TypeUtils.builtinToHostCType(dtypeId);
                    [nInstances, nElements] = this.writeStorageDefinition(cData, dataType, variableName);
                    assert(nInstances==1, 'Only an instance is supported for this type of CSCs');
                    % register getter to refresh the stored value
                    for idxElements=1:nElements
                        expressionValue = struct('Get', '', 'Set', '', 'IsParameterInterface', false);
                        if nElements == 1
                            leftOp = variableName;
                            expressionValue.Get = sprintf('%s = %s()', leftOp, impl.ReadExpression);
                        else
                            leftOp = sprintf('%s[%d]', variableName, idxElements-1);
                            expressionValue.Get = sprintf('%s = %s(%d)', leftOp, impl.ReadExpression, idxElements-1);
                        end
                        this.DefinedDataList(leftOp) = expressionValue;
                    end
                elseif rtw.connectivity.CodeInfoUtils.isa(impl, 'BasicAccessFunctionExpression')
                  cData = logData.getTargetDataElements();
                  if isempty(cData) || ~cData.isGetSetViaPointer
                    continue;
                  end
                  rtw.pil.OwnerAccessor.setOwner(impl, 'PIL');
                  if isempty(impl.DynamicMemory)
                    continue;
                  end
                  staticVar = impl.DynamicMemory.StaticVariable;
                  if ~isempty(staticVar) && ~this.isDefined(staticVar)
                    this.writeInterfaceData(logData, staticVar);
                    this.setImplementationOwner(staticVar, 'PIL');
                  end
                end
            end
            % add newline
            this.writer.newLine;
        end
        
        function writeSectionXILIOData(this)
            % Process input signals with DirectFeedThrough
            this.writeIOxilIOData('mdlOutputsInputs', ...
                'output_U');
            
            % Process input signals with no DirectFeedThrough
            this.writeIOxilIOData('mdlUpdateInputs',...
                'update_U');
            
            % Process output signals
            this.writeIOxilIOData('Outputs', 'Y');
            % Process output states
            this.writeIOxilIOData('States', 'S');
            
            % process const output signals
            this.writeConstOutputxilIOData;            
            % process init I/O
            this.writeFuncXILIOData(this.codeInfo.InitializeFunctions, 'init');            
            % process term I/O
            this.writeFuncXILIOData(this.codeInfo.TerminateFunctions, 'term');      
            % process workspace parameters for dynamic initialization /
            % tuning
            tag = '_ws_params';            
            tunableParamsIndices = this.SILPILWrapperUtils.getSupportedTunableWorkspaceParams;
            instPArgsIndices = this.SILPILWrapperUtils.getInstPArgumentIndices();
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                [tunableParamsIndices, instPArgsIndices], 'parameter');
            if ~isempty(dataElements)
                this.writePILIODataTable(dataElements, tag);
            end
        end
        
        function writeSectionUserFunctions(this)
            this.InterfaceStrategy.writeSectionUserFunctions(this.writer);
        end
        
        function writeSectionProcessParams(this)
            this.writer.writeCellLines({'/* In-the-Loop Interface functions - see xil_interface.h */';
                '';
                'XIL_INTERFACE_ERROR_CODE xilProcessParams(uint32_T xilFcnId) {';});
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});            
            this.writeSectionProcessParamsBody;
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.decIndent;            
            this.writer.writeLine('}');                        
            this.writer.newLine;
        end
        
        function writeSectionProcessParamsBody(this)                                    
            % make any required assignments                                    
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(...
                this.SILPILWrapperUtils, ...
                this.getParamIndicesForStorage, 'parameter');
            this.writeInputAssignments(dataElements);                                    
        end    
        
        function memcpyExpression = getCallerInputArgMemcpyExpression(this,...
                argInfo, argDataInterface, argCData)
            % return the appropriate memcpy expression            
            
            if argInfo.IsInOutArg
                [inputAddressValue, outputAddressValue] =....
                    getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                    argInfo, argDataInterface, argCData);
            else
                [inputAddressValue, outputAddressValue] =....
                    getCallerInputArgMemcpyInAndOutAddressValues(this,...
                    argInfo, argDataInterface, argCData);
            end
            memcpyExpression = ['memcpy(' outputAddressValue ', '...
                inputAddressValue ', (' argInfo.NumElements ...
                ') * sizeof(' argInfo.ArgBaseType '));'];
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerInputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSL>
            
            tgtVarName = argInfo.Implementation.Identifier;
            srcArgVarExpr = argInfo.ArgName;
            if(argInfo.IsVoidPointer)
                % cast to actual type to avoid compiler warnings regarding
                % "void *" type and also allow non-zero indexing for arrays
                srcArgVarExpr = sprintf('((%s)%s)',argInfo.ArgType,srcArgVarExpr);
            end
            if argInfo.IsBus
                inputAddressValue = srcArgVarExpr;
                outputAddressValue = ['&(' tgtVarName ')'];
            else
                outputAddressValue = ['&(' this.getElementExpression(argInfo.Implementation, ...
                    argCData, ...
                    0)  ')'];
                if argInfo.Implementation.Type.isPointer || this.isArrSizeOne(argInfo)
                    % Follow FPC Type
                    inputAddressValue = srcArgVarExpr;
                else
                    inputAddressValue = strrep(outputAddressValue, tgtVarName, srcArgVarExpr);
                end
            end
        end
        
        function [inputType, baseInputType] = getCallerInputArgType(...
                this, argInfo, argDataInterface, argCData) 
            
            [inputType, baseInputType] = this.InterfaceStrategy.getCallerInputArgType( ...
                argInfo, argDataInterface, argCData);
        end
        
        function [inputAddressValue, outputAddressValue] =....
                getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData) %#ok<INUSL>
            srcArgVarExpr = argInfo.ArgName;
            if(argInfo.IsVoidPointer)
                % cast to actual type to avoid compiler warnings regarding
                % "void *" type and also allow non-zero indexing for arrays
                srcArgVarExpr = sprintf('((%s)%s)',argInfo.ArgType,srcArgVarExpr);
            end
            tgtVarName = argInfo.Implementation.Identifier;
            if (argInfo.IsVector && ~argInfo.IsBus)
                outputAddressValue = ['&(' this.getElementExpression(argInfo.Implementation, ...
                    argCData, ...
                    0)  ')'];
                if(this.isArrSizeOne(argInfo))
                    % Follow FPC Type
                    inputAddressValue = srcArgVarExpr;
                else
                    inputAddressValue = strrep(outputAddressValue, tgtVarName, srcArgVarExpr);
                end
            else
                inputAddressValue = srcArgVarExpr;
                outputAddressValue = ['&(' tgtVarName ')'];
            end
        end
            
        function memcpyExpression = getCallerOutputArgMemcpyExpression(this,...
                argInfo, argDataInterface, argCData)
            [inputAddressValue, outputAddressValue] =....
                getCallerOutputArgMemcpyInAndOutAddressValues(this,...
                argInfo, argDataInterface, argCData);
            memcpyExpression = ['memcpy(' inputAddressValue ...
                ', ' outputAddressValue  ', (' argInfo.NumElements ...
                ') * sizeof(' argInfo.ArgBaseType '));'];
        end
        
        function [outputType, baseOutputType] = getCallerOutputArgType(this,...
                argInfo, argDataInterface, argCData) 
            
            
             [outputType, baseOutputType] = this.InterfaceStrategy.getCallerOutputArgType( ...
                argInfo, argDataInterface, argCData);
            
                
        end
        
        function storageSizeStr = getCallerArgStorageSizeString(this, argInfo)
            
            storageSizeStr = this.InterfaceStrategy.getCallerArgStorageSizeString(argInfo);

        end
        
        function returnArgType = getCallerFunctionReturnArgType(this, callerFunctionInfo)  
            returnArgType = this.InterfaceStrategy.getCallerFunctionReturnArgType( callerFunctionInfo);
        end
        
        function writeSectionCallerFunctionTrailerReturnArg(this, callerFunctionInfo)
            this.InterfaceStrategy.writeSectionCallerFunctionTrailerReturnArg(...
                this.writer, callerFunctionInfo)
        end
        
        function writeSectionCallerFunctionHeader(this, callerFunctionInfo, inArgsMemcpy)
            % copy input arguments to XIL local storage
            for ii = 1:(callerFunctionInfo.getNumInArgs + callerFunctionInfo.getNumInOutArgs)
                this.writer.writeLine(inArgsMemcpy{ii});
            end
            
            % make any required assignments
            % Note that if server is on target, there is no need to write 
            % output assignments because in this case there is no transfer
            % of data between host/target
            if ~callerFunctionInfo.getIsDefined
                dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                this.writeOutputAssignments(dataElements);
            end
        end
        
        function writeSectionCallerFunctionBody(this, callerFunctionInfo)
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;  
            this.InterfaceStrategy.writeSectionCallerFunctionBody( ...
                this.writer, isExecutionProfilingEnabled, callerFunctionInfo);
        end
        
        function writeSectionCallerFunctionTrailer(this, callerFunctionInfo, outArgsMemcpy)
            % make any required assignments
            dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);
            
            % If this function is defined, there is no need to 
            % make any assignments for the return argument because
            % we simply return back the LHS of the called function.
            % For example, in code we will have something like this:
            %
            %    errorStatus = Rte_Call_Foo(...) {
            %
            %    /* call Foo on the target */
            %    xsd_errorStatus = Foo(...)
            %
            %    return xsd_errorStatus;
            %   }            
            if callerFunctionInfo.getIsDefined
                [hasReturnArg, codeInfoArg] = callerFunctionInfo.hasReturnArg;
                if hasReturnArg
                    returnArgDataElements = callerFunctionInfo.getDataElementsForArgs(...
                        callerFunctionInfo.getCodeArgPosition(codeInfoArg));
                    dataElements = setdiff(dataElements, returnArgDataElements, 'stable');
                end
            end
            
            % if server is on target, there is no need to write input
            % assignments because in this case there is no transfer of data
            % between host/target
            if ~callerFunctionInfo.getIsDefined
                this.writeInputAssignments(dataElements);
            end
            
            % copy from XIL local storage to output arguments
            for ii = 1:(callerFunctionInfo.getNumOutArgs + callerFunctionInfo.getNumInOutArgs)
                this.writer.writeLine(outArgsMemcpy{ii});
            end
            
            % finally, return the LHS argument if required
            this.writeSectionCallerFunctionTrailerReturnArg(callerFunctionInfo);                        
        end
        
        function writeSectionCallerFunctions(this)
            functionNames = {};
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            for callerFcnIdx = 1:length(callerFunctionsInfo)
                callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                functionName = callerFunctionInfo.getTargetFunctionName;
                
                if ~ismember(functionName, functionNames)
                    % filter duplicates
                    functionNames{end+1} = functionName; %#ok<AGROW>
                    
                    % create argString
                    argString = '';
                    inArgsMemcpy = [];
                    outArgsMemcpy = [];
                    args = this.codeInfoUtils.getArray(callerFunctionInfo.getCodeInfoData.Prototype.Arguments);
                    for codeArgPosition = 1:length(args)
                        % get the DataInterface and DataElements for this
                        % arg
                        arg = args(codeArgPosition);
                        argInfo = this.getCallerArgInfo(callerFunctionInfo, arg, codeArgPosition);
                        
                        if codeArgPosition ~= 1
                            % comma separate args
                            argString = [argString ', '];  %#ok<AGROW>
                        end
                        
                        % put it all together
                        if argInfo.IsVoidPointer
                            voidPtrTypeStr = strrep(argInfo.ArgType,argInfo.ArgBaseType,'void');
                            argString = [argString voidPtrTypeStr ' ' argInfo.ArgName]; %#ok<AGROW>
                        else
                            argString = [argString argInfo.ArgType ...
                                ' ' argInfo.ArgName argInfo.StorageSizeStr]; %#ok<AGROW>
                        end
                        
                        % collect memcpy expressions
                        IsInOutArg =  strcmp(arg.IOType, 'INPUT_OUTPUT') || isequal(arg.IOType, ...
                            coder.descriptor.types.IOTypes.INPUT_OUTPUT);
                        if IsInOutArg
                            inArgsMemcpy{end+1} = argInfo.MemcpyExpression{1}; %#ok<AGROW>
                            outArgsMemcpy{end+1} = argInfo.MemcpyExpression{2}; %#ok<AGROW>
                        else
                            if argInfo.IsInArg
                                inArgsMemcpy{end+1} = argInfo.MemcpyExpression; %#ok<AGROW>
                            else
                                outArgsMemcpy{end+1} = argInfo.MemcpyExpression; %#ok<AGROW>
                            end
                        end
                    end
                    
                    % get the caller function return argument type
                    returnArgType = this.getCallerFunctionReturnArgType(callerFunctionInfo);
                    
                    % write function prototype
                    this.writer.wBlockStart('%s %s(%s)', returnArgType, functionName, argString);
                    this.writer.newLine;
                    
                    % write header
                    this.writeSectionCallerFunctionHeader(callerFunctionInfo, inArgsMemcpy);
                    
                    % write body
                    this.writeSectionCallerFunctionBody(callerFunctionInfo);
                    
                    % write trailer
                    this.writeSectionCallerFunctionTrailer(callerFunctionInfo, outArgsMemcpy);
                                                       
                    this.writer.wBlockEnd(); %function
                end
            end
        end
        
        function writeSectionGetDataTypeInfo(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilGetDataTypeInfo(void) {');
            this.writer.incIndent;            
            this.writeSectionGetDataTypeInfoBody;
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.newLine;                        
        end
        
        function writeSectionGetDataTypeInfoBody(this)
            % send back XIL_RESPONSE_TYPE_SIZE for required floating point
            % types                                    
            this.writeSectionResponseTypeSize('single');
            this.writeSectionResponseTypeSize('double');            
        end                
        
        function writeSectionInit(this)
            this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilInitialize(uint32_T xilFcnId)');            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionInitBody;
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd(); % XIL_INTERFACE_ERROR_CODE xilInitialize(uint32_T xilFcnId)
        end
                
        
        function [sectionIdForFunction, instrumentationCalls] = getProfilingIdentifier...
                (this, codeInfoForFunction)
            
            functionInfoForProfiling = coder.profile.ExecTimeConfig ...
                .getFunctionInfoForProfiling(codeInfoForFunction);
            functionName = functionInfoForProfiling.taskName;
            % Get the model name or (in the case of right-click build) SID for the subsystem
            % path
            lComponentRef = this.SILPILWrapperUtils.getSILPILInterface().getComponentReferenceSid();
            if this.SILPILWrapperUtils.StackProfilingEnabled
                lStackTaskRegistry = this.SILPILWrapperUtils.getProfilingStackTaskRegistry;
                sectionIdForFunction = lStackTaskRegistry.requestTaskStackProbe(functionName, functionInfoForProfiling, lComponentRef);
                instrumentationCalls = lStackTaskRegistry.instrumentationFnc(coder_profile_ProbeType.TASK_STACK_PROBE, sectionIdForFunction);
            else
                lTaskRegistry = this.SILPILWrapperUtils.getProfilingTaskRegistry;
                sectionIdForFunction =...
                    lTaskRegistry.requestIdentifierForTask(functionName, ...
                                                           functionInfoForProfiling, ...
                                                           lComponentRef);
                instrumentationCalls = lTaskRegistry.instrumentationFnc(...
                    coder_profile_ProbeType.TASK_TIME_PROBE,...
                    sectionIdForFunction);
            end
        end
        
        function writeSectionOverheadConfiguration(this)
            cfg = this.SILPILWrapperUtils.getTargetServices().getProfilingService();
            if cfg.isOverheadFilteringActive() && ...
                    ~strcmp(this.SILPILWrapperUtils.CodeProfilingSaveOptions, 'MetricsOnly')
                this.writer.wBlockStart('');
                if cfg.isOverheadBenchmarkExecuted()
                    lComponentRef = this.SILPILWrapperUtils.getSILPILInterface().getComponentReferenceSid();
                    % retrieve the Overhead Registry, deploy the specific
                    % probes and generate the call to run the benchmark
                    lOverheadRegistry = this.SILPILWrapperUtils.getProfilingOverheadRegistry;
                    idDmy = lOverheadRegistry.requestDummyOverheadProbe(lComponentRef);
                    idTmr = lOverheadRegistry.requestTimerOverheadProbe(lComponentRef);
                    idFrz = lOverheadRegistry.requestFreezeOverheadProbe(lComponentRef);
                    idTsk = lOverheadRegistry.requestTaskOverheadProbe(lComponentRef);
                    idFcn = lOverheadRegistry.requestFunctionOverheadProbe(lComponentRef);
                    numIterations = cfg.getOverheadBenchmarkSteps();
                    this.writer.wLine('xilProfilingEstimateOverhead(%d, %d, %d, %d, %d, %d);', ...
                        idTmr, idFrz, idTsk, idFcn, idDmy, numIterations);
                else
                    % use the average overheads that are already set in
                    % TargetConnectivity
                    [tsk, fcn, frz] = cfg.getOverheads();
                    this.writer.wLine('xilProfilingOverheadSetting(%d, %d, %d);', ...
                        tsk, fcn, frz);
                end
                this.writer.wBlockEnd; % overhead setting block
            end
        end
        
        function writeSectionPointerExpressionAssignments(this)
            % assign the rtM pointer fields to the variables allocated for
            % the groups and internal data variables
            compInterface = this.codeInfoUtils.getCodeInfo();
            if isempty(compInterface.AllocationFunction)
                
                % initialize the RTM pointer itself if necessary
                this.writeRTMPointerAssignmentHook();
                
                % assign rtM pointer fields to group structs and other 
                % internal data
                this.writer.wBlockStart('');
                dataInterface = [compInterface.Inports, ...
                    compInterface.Outports, ...
                    compInterface.InternalData, ...
                    compInterface.Parameters, ...
                    compInterface.DataStores];
                this.writePointerExpressionAssignments(dataInterface);
                this.writer.wBlockEnd();
            end
        end
        
        function writePointerExpressionAssignments(this, interfaces)
            % write assignments for PointerExpression interfaces
            for i = 1:length(interfaces)
                pointerExpression = rtw.connectivity.CodeInfoUtils.getNestedImplementation(interfaces(i).Implementation, 'PointerExpression');
                if ~isempty(pointerExpression)
                    prtExpr = pointerExpression.getExpression();
                    % Keep track of assigned pointers to avoid writing the same
                    % assignment multiple times.
                    if ~ismember(prtExpr, this.DynamicallyAssignedPointersList)
                        this.DynamicallyAssignedPointersList{end+1} = prtExpr;
                        tgtExpr = pointerExpression.TargetRegion.getExpression();
                        this.writer.writeLine('%s = &%s;', ...
                            prtExpr, ...
                            tgtExpr);
                    end
                end
            end
        end
        
        function writePointerVariableAssignments(this)
            % Assign pointers to variable owned by xil_interface. This is
            % needed in cases where the component owns uninitialized
            % pointers that need to be assigned to memory regions owned by
            % xil_interface.
            % This patten has been so far observed only with IO ports.
            
            dataIntefaces = [ ...
                this.codeInfo.Inports, ...
                this.codeInfo.Outports];
            
            writeComment = true;
            for idx = 1:numel(dataIntefaces)
                implementation = dataIntefaces(idx).Implementation;
                if rtw.connectivity.CodeInfoUtils.isa(implementation, 'PointerVariable')
                    pointerOwner = this.getImplementationOwner(implementation);
                    targetVarOwner = this.getImplementationOwner(implementation.TargetVariable);
                    if ~strcmp(pointerOwner, targetVarOwner)
                        if writeComment
                            this.writer.newLine();
                            this.writer.wComment('Assign pointers to variables owned by In-the-Loop');
                            writeComment = false;
                        end
                        this.writer.writeLine('%s = &(%s);', ...
                            implementation.getExpression(), ...
                            implementation.TargetVariable.getExpression());
                    end
                end
            end
        end
        
        function writeSectionInitBody(this)
            % assign uninitialized pointers
            this.writePointerVariableAssignments();
            
            % assign rtM pointers to the memory allocated for CoderDataGroups 
            this.writeSectionPointerExpressionAssignments();

            % initialize any output storage owned by In-the-Loop
            this.writeSectionExternalInitialization;
            
            % call allocation function if one exists before calling initialize
            % functions
            this.writeSectionAllocationFunction;

            % call initialize method for instrumentation
            if this.SILPILWrapperUtils.AreEventCallbacksRequired
                this.writer.wComment('Initialize instrumentation');
                this.writer.writeLine('InitEvent();');
            end

            if this.SILPILWrapperUtils.isTimeProfilingEnabled
                % if profiling is active, the overhead can be filtered
                this.writeSectionOverheadConfiguration();
            end
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled; 
            
            % SLDV case: don't perform the function id check to avoid
            % imprecision during the alias analysis
            if ~this.SILPILWrapperUtils.getSILPILInterface.IsForSldvAnalysis
                this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                    'if (xilFcnId == 0) {'});
                this.writer.incIndent;
            else
                this.writer.writeLine('(void)xilFcnId;');
            end

            % call initialize functions
            initFuns = this.codeInfo.InitializeFunctions; 
            if isempty(initFuns)
                this.writeEmptyFunctionCall;                
            else
                
                % make any required assignments
                inputIndices = this.codeInfoUtils.getDataInterfacesForFunction(initFuns, 'input');
                inputIndices = this.codeInfoUtils.getAdditionalInitializeInportIndices(inputIndices);
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             inputIndices, 'input');

                dataElements = [dataElements, rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread')];
                this.writeInputAssignments(dataElements);
                % params
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.getParamIndicesForFunction(initFuns), 'parameter');
                this.writeInputAssignments(dataElements);                
                isSetBeforeInit = true; %Signal this is a call Set functions in Init.
                this.writeClassGetSetCalls('Set', isSetBeforeInit); %Must be final call before function is called.
                for i=1:length(initFuns)
                    fun = initFuns(i);    
                    
                    if isExecutionProfilingEnabled
                        
                        [sectionIdForFunction, instrCalls] =...
                            this.getProfilingIdentifier(fun);
                            
                        this.writer.writeLine('%s(%dU);', ...
                                              instrCalls{1}, ...
                                              sectionIdForFunction);                        
                    end
                    
                    this.writeSectionInitializeFunctionCall(fun);
                    
                    if isExecutionProfilingEnabled
                        this.writer.writeLine('%s(%dU);',...
                                              instrCalls{2},...
                                              sectionIdForFunction);
                    end
                end
            end
            isSetBeforeInit = false;
            this.writeClassGetSetCalls('Get', isSetBeforeInit); %Must be first code after function called
                
            % make any required assignments
            %
            % constant and non-constant outputs may have been updated; make
            % sure I/O data for ALL outports is up to date 
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         1:length(this.codeInfo.Outports), 'output');

            % also include constant logging data which is set during
            % initialization
            loggingData = this.SILPILWrapperUtils.getLoggingData;
            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'));
            % add all indexes for state logging (those uploaded before code
            % execution) because they have an initial state which must be
            % uploaded before calling xilOutput for the first time (this
            % initial state is set in mdlInitialize)
            stateLoggingIndexes = this.codeInfoUtils.getAllPreDataStateLoggingIndices();
            loggingDataIndices = unique([loggingDataIndices, stateLoggingIndexes]);
            selectedLoggingData = loggingData(loggingDataIndices);
            selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
            for i=1:length(selectedLoggingData)
                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
            end

            dataElements = [dataElements rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite')];                                                     
            this.writeOutputAssignments(dataElements);

            % Update the error status for the non-SLDV case
            if ~this.SILPILWrapperUtils.getSILPILInterface.IsForSldvAnalysis
                this.writer.decIndent;
                this.writer.writeCellLines({'}' ...
                    'else {'});
                this.writer.incIndent;
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end                              
        
        function writeSectionExternalInitialization(this)  
            this.writer.writeLine('/* initialize output storage owned by In-the-Loop */');
           
            %Initialize all local method access variables
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.Outports), 'output');
            this.writeDataInterfaceDefaultInitialization(dataElements);
            % initialize output sizes owned by In-the-Loop
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getOutportSizes), ...
               'outputsize');
            this.writeDataInterfaceDefaultInitialization(dataElements);
            this.InterfaceStrategy.writeSectionExternalInitialization(this.writer);
            
            % Call service pre-initialize if required
            if ~isempty(this.ServicePreInitFunctionCall)
                this.writer.wLine('%s;', this.ServicePreInitFunctionCall);
            end
        end
        
        function writeSectionAllocationFunction(this)
            %Call Allocation Function and check memory allocation was
            %successful.
            
            allocFunction = this.codeInfo.AllocationFunction;
            if ~isempty(allocFunction)
                
                %Should only ever have one Allocation Function, code below
                %will need to change if that isn't true anymore.
                assert( isscalar(allocFunction) , 'SIL only supports one Allocation Function');
                
                this.writer.wComment('Allocate model data');
                this.writer.wLine('%s;', allocFunction.getFunctionCall);
                allocReturn = allocFunction.ActualReturn.Implementation.Identifier;
                this.writer.wBlockStart('if ( %s == NULL )', allocReturn);
                this.writer.wLine('return XIL_INTERFACE_MEMORY_ALLOCATION_ERROR;');
                this.writer.wBlockEnd;
                
                if strcmp(this.SILPILWrapperUtils.getConfigInterface.getParam('SuppressErrorStatus'), 'off')
                    if isfield(this.SILPILWrapperUtils.getInfoStruct.modelInterface, 'UseMemberFunctionsForRTM') && ...
                            this.SILPILWrapperUtils.getInfoStruct.modelInterface.UseMemberFunctionsForRTM
                        this.writer.wBlockStart('if ( ( %s )->getErrorStatus() != NULL )', allocReturn);                        
                    else
                        this.writer.wBlockStart('if ( rtmGetErrorStatus( %s ) != NULL )', allocReturn);
                    end
                    this.writer.wLine('return XIL_INTERFACE_MEMORY_ALLOCATION_ERROR;');
                    this.writer.wBlockEnd;
                end
                
            end
            
        end                
        
        function writeEmptyFunctionCall(this)
            this.writer.writeLine('/* No Function to Call */');            
        end
        
        function writeSectionSystemResetFunction(this)
            % may be overridden
            this.writeEmptyFunctionCall;
        end                         
        
        function writeSectionSystemInitializeFunction(this)
            % may be overridden
            this.writeEmptyFunctionCall;
        end
        
        function writeSectionSystemInitializeInitStates(this)
            % may be overridden
            this.writeEmptyFunctionCall;
        end
            
        function writeSectionSystemInitializeTrailer(this) %#ok<MANU>
            % see subclass            
        end
        
        function writeSectionInitUDataProcessing(this)
            this.writer.wBlockStart(['XIL_INTERFACE_ERROR_CODE xilGetHostToTargetData(uint32_T xilFcnId, ' ...
                                                                           'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                           'uint32_T xilCommandIdx, ' ...
                                                                           'XILIOData ** xilIOData)']);
            this.writer.writeCellLines({'XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;';
                '*xilIOData = 0;';
                '/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   errorCode = XIL_INTERFACE_UNKNOWN_FCNID;';
                '   return errorCode;';
                '}'});

            % add the cases
            this.InSwitchStatement = false;
            commandIdxSwitchGenerated = this.writeSectionInitUDataProcessingCases;

            if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');  

                if ~commandIdxSwitchGenerated
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                end
            else
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
            end
            
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd; %HostToTarget Function
            this.writer.newLine;
        end

        function writeSectionInitDataProcessingSwitchStart(this)
            if (~this.InSwitchStatement)
                % write the start of the switch case statement:
                this.writer.writeLine('switch(xilCommandType) {');
                % switch indent
                this.writer.incIndent;    
                this.InSwitchStatement = true;
            end
        end
 
        function paramIndices = getParamIndicesForFunction(this, functionInterface)
            % use codeInfoUtils implementation to get parameters
            paramIndices = this.codeInfoUtils.getParamIndicesForFunctionImpl(...
                functionInterface, this.SILPILWrapperUtils);
        end
        
        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCases(this)
            % support for InitializeFunction inputs
            writeSectionInitUDataProcessingCase(this, ...
                                                this.codeInfo.InitializeFunctions, ...
                                                'XIL_INITIALIZE_COMMAND', ...
                                                'xil_fcnid0_init');
            
            % support for TerminateFunctions inputs
            writeSectionInitUDataProcessingCase(this, ...
                                                this.codeInfo.TerminateFunctions, ...
                                                'XIL_TERMINATE_COMMAND', ...
                                                'xil_fcnid0_term');
                                                                  
            paramIndices = this.TransferInterfaceUtils.getParametersToTransfer(this.SILPILWrapperUtils);
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                paramIndices, 'parameter');
            
            if ~isempty(dataElements)
                isTargetToHost = false;
                % support for PROCESS_PARAMS command
                this.writeSectionInitDataProcessingCase('XIL_PROCESS_PARAMS_COMMAND', ...
                    'xil_fcnid0_ws_params', ...
                    dataElements, isTargetToHost);
            end
            
            % support for Task inputs
            
            tableName = 'xil_fcnid0_task';
            tasksOutput = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasksOutput = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasksOutput, ...
                'XIL_OUTPUT_COMMAND', ...
                tableName);
            
            
            updateTasks = this.codeInfoUtils.getUpdateTasks;            
            commandIdxSwitchGeneratedForTasksUpdate = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                updateTasks, ...
                'XIL_UPDATE_COMMAND', ...
                tableName);            
  
            commandIdxSwitchGeneratedForCallers = false;
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            if ~isempty(callerFunctionsInfo)
                tableNamePostFix = '_y';
                numCallerFcns = length(callerFunctionsInfo);                
                for callerFcnIdx = 1:numCallerFcns
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);                
                    commandIdxSwitchGeneratedForCallers =...
                        writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numCallerFcns, ...
                        num2str(callerFunctionInfo.getTID), ...
                        'XIL_CLIENT_SERVER_COMMAND', ...
                        dataElements,...
                        tableName,...
                        tableNamePostFix,...
                        commandIdxSwitchGeneratedForCallers);
                end
                if commandIdxSwitchGeneratedForCallers
                    % generate default
                    this.writer.writeCellLines({'default:';
                        '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                        '   break;'});
                    % switch indent
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
            end
            
            commandIdxSwitchGenerated = commandIdxSwitchGeneratedForCallers || ...
                commandIdxSwitchGeneratedForTasksOutput || commandIdxSwitchGeneratedForTasksUpdate ;
            
            if this.codeInfoUtils.isExportFunctions
                % support for Export Functions DisableFunction inputs
                %
                % map directly on to inputs for step command
                %
                this.writeSectionInitDataProcessingSwitchStart();
                this.writer.writeLine('case XIL_DISABLE_COMMAND: {');
                this.writer.incIndent;
                this.writer.writeLine('errorCode = xilGetHostToTargetData(xilFcnId, XIL_OUTPUT_COMMAND, xilCommandIdx, xilIOData);');
                this.writer.writeLine('return errorCode;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end                
               
        function writeSectionInitUDataProcessingCase(this, ...
                                                     functionInterfaces, ...                                                     
                                                     command, ...
                                                     tableName)
            % process inports and params              
            inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                                                                                 'inputsize'); 
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                                                                                 'outputsize');                                                                              
            inputIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, 'input');
            taskCounterOffsetDataElements = rtw.pil.DataElement.empty;
            if strcmp(command,'XIL_INITIALIZE_COMMAND')
                inputIndices = this.codeInfoUtils.getAdditionalInitializeInportIndices(inputIndices);
                taskCounterOffsetDataElements = this.getTaskCounterOffsetDataElements();
            elseif strcmp(command,'XIL_TERMINATE_COMMAND')
                inputIndices = unique([inputIndices, ...
                                this.codeInfoUtils.getInputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'))]);
            end
            
            
            dataElements = [taskCounterOffsetDataElements, ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inputIndices, 'input'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inportSizesIndices, 'inputsize'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, outportSizesIndices, 'outputsize'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    this.getParamIndicesForFunction(functionInterfaces), 'parameter')];

            if ismember(command, {'XIL_INITIALIZE_COMMAND', 'XIL_SYSTEM_INITIALIZE_COMMAND'})
                %Add Data Store Reads to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();
                dsReadIndices = [];
                for taskIdx = 1:length(initTask)
                    dsReadIndices = [dsReadIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dsread')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsReadIndices, 'stable'), 'dsread')];    
                if strcmp(command, 'XIL_INITIALIZE_COMMAND')
                    %Add InstP model arguments
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getInstPArgumentIndices(), 'parameter')];
                end
            elseif strcmp(command,'XIL_TERMINATE_COMMAND')
                %Add Data store Reads to TERMINATE Calls
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(termTask, 'term'), 'dsread'), 'dsread')]; %Extra Argument needed to workaround G1411813
                end
            end
                            
            if ~isempty(dataElements)           
                isTargetToHost = false;
                this.writeSectionInitDataProcessingCase(command, ...
                    [tableName '_u'], ...
                    dataElements, isTargetToHost);
            end
        end        
        
        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)                                                                                                                                      
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks
                task = tasks(taskIdx);                                
                
                id = int2str(task.id);
                % process inports, params and dsread
                % ports may not be part of the function signature
                
                
                portIndices = this.codeInfoUtils.getInputPortsForTask(task);                
                if (strcmp(command,'XIL_UPDATE_COMMAND')  )
                    
                    iotag = '_update_u';
                    portIndices = this.SILPILWrapperUtils.getNonDFInputPortsIndices(portIndices);
                    inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(portIndices);
                    dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        portIndices, 'input'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        inportSizesIndices, 'inputsize')];
                    
                else
                    iotag = '_output_u';
                    
                    portIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(portIndices);         
                    inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(portIndices);
                    outportIndices = this.codeInfoUtils.getOutputPortsForTask(task);
                    outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                    % process the dsRead for this task
                    dsReadIndices =  this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
                    % process any additional data interfaces
                    internalDataInterfaceIndices = ...
                        this.TransferInterfaceUtils.getInternalDataToUpload(this.codeInfo, task.codeInfoData.Timing);
                    
                    %Note: Internal data must be first in the list as it's only
                    %processed via Opaque Regions. So is immediately de-serialized.
                    dataElements = [this.TransferInterfaceUtils.getDataInterfaces(this.SILPILWrapperUtils, ...
                        internalDataInterfaceIndices, 'internal'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        portIndices, 'input'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        inportSizesIndices, 'inputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        outportSizesIndices, 'outputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.getParamIndicesForFunction(task.codeInfoData), 'parameter'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, dsReadIndices, 'dsread'),...
                        this.getServerInputArgsDataElement(task)];
                end
                
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        iotag,...
                        commandIdxSwitchGenerated);
                end
            end
            
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end

        function commandIdxSwitchGenerated = writeSectionInitUDataProcessingCaseWithCommandIdxSwitchBody(this, ...
                                                                                                         numTasks, ...
                                                                                                         taskId, ...
                                                                                                         command, ...
                                                                                                         dataElements,...
                                                                                                         tableName,...
                                                                                                         tableNamePostFix,...
                                                                                                         commandIdxSwitchGenerated)            
            if numTasks > 1
                if ~commandIdxSwitchGenerated
                    this.writeSectionInitDataProcessingSwitchStart();
                    this.writer.writeLine(['case ' command ': {']);
                    this.writer.incIndent;
                    % apply nested switch on "xilCommandIdx"
                    this.writer.writeLine('switch(xilCommandIdx) {');
                    % switch indent
                    this.writer.incIndent;
                    commandIdxSwitchGenerated = true;
                end
                caseId = taskId;
            else
                caseId = command;
            end
            isTargetToHost = false;
            this.writeSectionInitDataProcessingCase(caseId, ...
                [tableName taskId tableNamePostFix], ...
                dataElements, isTargetToHost);                        
        end
        
        function writeSectionInitDataProcessingCase(this, caseId, tableName, dataElements, isTargetToHost)  
            this.writeSectionInitDataProcessingSwitchStart();
            this.writer.wBlockStart(['case ' caseId ':']);
            
            this.writePILIODataTableInitialization(dataElements, tableName);
            this.writeTransferCalls(dataElements, isTargetToHost);
            this.writer.writeCellLines({['*xilIOData = &' tableName '[0];'];
                'break;'});
            
            this.writer.wBlockEnd; % case caseId
        end
                
        function writeSectionUploadProfilingDataPoint(this)

            isCodeInstrumentationEnabled =...
                this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                    this.SILPILWrapperUtils.isProfilingEnabled;
            if isCodeInstrumentationEnabled
                lTargetWordSize = this.SILPILWrapperUtils.getParam('TargetWordSize');
                lCodeInstrSizeCDataType = ...
                    coder.profile.ExecTimeConfig.getCodeInstrNumElsCDataType...
                    (lTargetWordSize);
                lIdType = ...
                    coder.profile.ExecTimeConfig.getIdentifierCDataType...
                    (lTargetWordSize);
                this.writer.writeLine...
                    ('void %s(void * pData, %s numMemUnits, %s sectionId) {', ...
                     rtw.pil.SILPILInterface.TargetCodeInstrFcnName,...
                     lCodeInstrSizeCDataType, lIdType);
                this.writer.incIndent;
                this.writer.newLine;
                               
                this.writer.writeLine('/* Send code instrumentation data to host */');                            
                dataForHost = {sprintf('(MemUnit_T *) &numMemUnits'), 'sizeof(numMemUnits)'};
                
                dataForHost(end+1,:) = ...
                    {sprintf('(MemUnit_T *) &sectionId'), ...
                     sprintf('sizeof(%s)', lIdType)};
                
                dataForHost(end+1,:) = ...
                    {sprintf('(MemUnit_T *) pData'), 'numMemUnits'};
                
                numDataItems = length(dataForHost(:,1));
                for i=1:numDataItems
                    tmpAddr=dataForHost{i,1};
                    tmpSize=dataForHost{i,2};
                    this.writer.writeLine...
                        (['if (codeInstrWriteData(%s, %s) '...
                          '!= XIL_DATA_STREAM_SUCCESS) {'], ...
                         tmpAddr, tmpSize);
                    this.writer.incIndent;              
                    this.writer.writeLine('for(;;);');
                    this.writer.decIndent;              
                    this.writer.writeLine('}');
                end
                this.writer.decIndent;                
                this.writer.writeLine('}');
                this.writer.newLine;
                
            end
        end
        
        function writeSectionPause(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilPause(uint32_T xilFcnId) {');
            this.writer.incIndent;
            this.writeSectionPauseBody;
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.newLine;
        end
        
        function writeSectionPauseBody(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writer.wBlockStart('if (xilFcnId == 0)');
            if this.SILPILWrapperUtils.AreEventCallbacksRequired
                this.writer.writeLine('PauseEvent();');
            else
                this.writer.writeLine('/* Nothing to do */');
            end
            this.writer.wBlockMiddle('else');
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.wBlockEnd;
            this.writer.writeLine('return errorCode;');
        end

        function writeSectionOutput(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilOutput(uint32_T xilFcnId, uint32_T xilTID) {');
            % fun indent
            this.writer.incIndent;
            this.writeSectionOutputBody;                        
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end                          
        
        function writeSectionOutputBody(this)
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});
            
            outputTasks = this.getOutputTasks;
            
            if isempty(outputTasks)
                this.writer.writeLine('/* No Output Function - this function should never be called */');
                this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
                this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
            else
                
                this.writer.writeLine('switch(xilTID) {');
                % switch indent
                this.writer.incIndent;
                                               
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                
                % check to make sure we never get an update function for C++ encapsulated mode:
                if this.SILPILWrapperUtils.isCPPEncap
                    assert(isempty(this.codeInfoUtils.getUpdateTasks), ...
                           'Expected no update tasks for C++ class generation');
                    isCombinedOutputUpdate = true;
                else
                    isCombinedOutputUpdate = strcmp(this.SILPILWrapperUtils.getParam( ...
                        'CombineOutputUpdateFcns'), 'on');
                end

                for taskIdx = 1:length(outputTasks)
                    outputTask = outputTasks(taskIdx);                    
                                                                                
                    this.writer.writeLine(['case ' int2str(outputTask.id) ':']);
                    this.writer.incIndent;
                    
                    
                    if rtw.pil.CodeInfoData.isInterfaceActive(outputTask.codeInfoData)
                         % make any required assignments
                        this.writeSectionOutputInputAssignments(outputTask);
                        isSetBeforeInit = false;
                        this.writeClassGetSetCalls('Set', isSetBeforeInit); %Must be final thing before function called
                        if isExecutionProfilingEnabled

                            [sectionIdForFunction, instrCalls] =...
                                this.getProfilingIdentifier(...
                                    outputTask.codeInfoData);

                            this.writer.writeLine('%s(%dU);', ...
                                                  instrCalls{1},...
                                                  sectionIdForFunction);
                        end

                        % call output function
                        this.writeSectionOutputFunctionCall(taskIdx, outputTask);
                        if isExecutionProfilingEnabled
                            this.writer.writeLine('%s(%dU);',...
                                                  instrCalls{2},...
                                                  sectionIdForFunction);
                        end
                        isSetBeforeInit = false;
                        this.writeClassGetSetCalls('Get', isSetBeforeInit); %Must be first thing after function called
                        % make any required assignments
                        this.writeSectionOutputOutputAssignments(outputTask);


                        % we only need to call rt_UpdateTXYLogVars if 
                        % CombineOutputUpdate is off. Also, we only call this
                        % for the base rate
                        if this.SILPILWrapperUtils.isMatFileLoggingEnabled &&...
                                (taskIdx==1) && ~isCombinedOutputUpdate
                            this.writeSectionOutputBodyMatFileLogging();
                        end
                    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('}');

                if this.SILPILWrapperUtils.AreEventCallbacksRequired
                    this.writer.writeLine('StepCompletedEvent();');
                    this.writer.newLine;
                end
                
                this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            end            
        end
        
        function writeSectionOutputInputAssignments(this, outputTask)
            
            % Update all input setters
            
            inportIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(this.codeInfoUtils.getInputPortsForTask(outputTask));
            
            % inputs
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         inportIndices, 'input');
            this.writeInputAssignments(dataElements);
            % params
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         this.getParamIndicesForFunction(outputTask.codeInfoData), 'parameter');
            this.writeInputAssignments(dataElements);
            % datastores
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getReaderSharedDataStoresForTask(outputTask), 'dsread'), 'dsread');
            this.writeInputAssignments(dataElements);

            % server input arguments
            dataElements = this.getServerInputArgsDataElement(outputTask);
            this.writeInputAssignments(dataElements);                        
        end
        
        function writeSectionUpdateInputAssignments(this, updateTask)
            inportIndices = this.SILPILWrapperUtils.getNonDFInputPortsIndices(this.codeInfoUtils.getInputPortsForTask(updateTask));
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inportIndices, 'input');
            this.writeInputAssignments(dataElements);
        end
                        
        function writeSectionOutputFunctionCall(this, taskIdx, outputTask)  %#ok<INUSL>                        
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, outputTask.codeInfoData);            
            if isServerFunction
                % g1051905 (item 5): getFunctionCall errors out for server OutputFunction in codeInfo.
                this.writer.writeLine([this.getFunctionCallExpressionForServerFunction(serverFunctionInfo) ';']);
            elseif this.codeInfoUtils.needsCustomFunctionCall(outputTask.codeInfoData, ...
                    @(x)this.InterfaceStrategy.isInternalDataWithMappingToRTE(x))
                this.writeCustomFunctionCall(outputTask.codeInfoData);
            else
                this.writer.writeLine([outputTask.codeInfoData.getFunctionCall() ';']);
            end
        end
        
        function writeSectionInitializeFunctionCall(this, initFun)
            if this.codeInfoUtils.needsCustomFunctionCall(initFun, ...
                    @(x)this.InterfaceStrategy.isInternalDataWithMappingToRTE(x))
                this.writeCustomFunctionCall(initFun);
            else
                this.writer.writeLine([initFun.getFunctionCall() ';']);
            end 
        end

        function writeSectionOutputOutputAssignments(this, outputTask)
            % outputs
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                         this.codeInfoUtils.getOutputPortsForTask(outputTask), 'output');
            this.writeOutputAssignments(dataElements);
            % datastores
            dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getWriterSharedDataStoresForTask(outputTask), 'dswrite'), 'dswrite');
            % logging data             
            loggingData = this.SILPILWrapperUtils.getLoggingData;                
            loggingDataIndices = this.codeInfoUtils.getLoggingForTask(outputTask);
            selectedLoggingData = loggingData(loggingDataIndices);
            selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
            for i=1:length(selectedLoggingData)
                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
            end
            this.writeOutputAssignments(dataElements);
            
            dataElements = this.getServerOutputArgsDataElement(outputTask);
            this.writeOutputAssignments(dataElements);
        end
        
        function writeSectionUpdate(this)
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilUpdate(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}'});
            
            updateTasks = this.codeInfoUtils.getUpdateTasks;
            
            if isempty(updateTasks)
                this.writer.writeLine('/* No Update Function */');
                this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            else
                this.writer.writeLine('switch(xilTID) {');
                this.writer.incIndent;                
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                
                for taskIdx = 1:length(updateTasks)
                    updateTask = updateTasks(taskIdx);
                    
                    this.writer.writeLine(['case ' int2str(updateTask.id) ':']);
                    this.writer.incIndent;
                    
                    if rtw.pil.CodeInfoData.isInterfaceActive(updateTask.codeInfoData)
                    % make any required assignments
                        this.writeSectionUpdateInputAssignments(updateTask);

                        if isExecutionProfilingEnabled

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

                        this.writeSectionUpdateFunctionCall(taskIdx, updateTask);
                        if isExecutionProfilingEnabled
                            this.writer.writeLine('%s(%dU);',...
                                                  instrCalls{2}, ...
                                                  sectionIdForFunction);
                        end
                    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('}');
            end
            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionUpdateFunctionCall(this, taskIdx, updateTask)  %#ok<INUSL>
            if this.codeInfoUtils.needsCustomFunctionCall(updateTask.codeInfoData, ...
                    @(x)this.InterfaceStrategy.isInternalDataWithMappingToRTE(x))
                this.writeCustomFunctionCall(updateTask.codeInfoData);
            else
                this.writer.writeLine([updateTask.codeInfoData.getFunctionCall ';']);
            end
        end
        
        function writeSectionInitTargetData(this)
            this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilInitTargetData(void)');

            this.writeSectionInitTargetDataBody();

            this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.wBlockEnd();
            this.writer.newLine;
        end

        function writeSectionInitSDataProcessing(this)
            this.writer.wBlockStart(['XIL_INTERFACE_ERROR_CODE xilGetTargetToHostPreData(uint32_T xilFcnId, ' ...
                                                                          'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                          'uint32_T xilCommandIdx, ' ...
                                                                          'XILIOData ** xilIOData, ' ...
                                                                          'MemUnit_T responseId, ' ...
                                                                          'uint32_T serverFcnId)']);
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writer.writeLine('*xilIOData = 0;');
            this.writer.wBlockStart('if (xilFcnId != 0)');
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd();

            this.InSwitchStatement = false;
            tableName = 'xil_fcnid0_task';
            tasks = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasks = writeSectionInitSDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasks, ...
                'XIL_OUTPUT_COMMAND', ...
                 tableName);
             
             if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');

                if ~commandIdxSwitchGeneratedForTasks
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                    this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                    this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
                end
            else
                % mark xilIOData arg as unused
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
            end
                             
            this.writer.writeLine('return errorCode;');
            this.writer.wBlockEnd();
            this.writer.newLine;
        end
        
        function writeSectionInitYDataProcessing(this)
            this.writer.writeLine(['XIL_INTERFACE_ERROR_CODE xilGetTargetToHostData(uint32_T xilFcnId, ' ...
                                                                          'XIL_COMMAND_TYPE_ENUM xilCommandType, ' ...
                                                                          'uint32_T xilCommandIdx, ' ...
                                                                          'XILIOData ** xilIOData, ' ...
                                                                          'MemUnit_T responseId, ' ...
                                                                          'uint32_T serverFcnId) {']);
            this.writer.incIndent;
            this.writer.writeCellLines({'XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;';
                '/* Single In-the-Loop Component */';
                '*xilIOData = 0;';
                'if (xilFcnId != 0) {';
                '   errorCode = XIL_INTERFACE_UNKNOWN_FCNID;';
                '   return errorCode;';
                '}'});
            
            % add the cases
            this.InSwitchStatement = false;
            commandIdxSwitchGenerated = this.writeSectionInitYDataProcessingCases;                        
            
            if this.InSwitchStatement
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');

                if ~commandIdxSwitchGenerated
                    % mark xilIOData arg as unused
                    this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                    this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                    this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
                end
            else
                % mark xilIOData arg as unused
                this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_TID;');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandType);');
                this.writer.writeLine('UNUSED_PARAMETER(xilCommandIdx);');
                this.writer.writeLine('UNUSED_PARAMETER(responseId);');
                this.writer.writeLine('UNUSED_PARAMETER(serverFcnId);');
            end
            this.writer.writeLine('return errorCode;');
            % fun indent
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function commandIdxSwitchGenerated = writeSectionInitYDataProcessingCases(this)
            isTargetToHost = true;
            if this.codeInfoUtils.hasConstOutput
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             this.codeInfoUtils.getOutputPortsForRates(this.codeInfoUtils.getRates('ONESHOT')), ...
                                                             'output');
                if ~isempty(dataElements)
                    % support for const outputs
                    this.writeSectionInitDataProcessingCase('XIL_CONST_OUTPUT_COMMAND', ...
                        'xil_fcnid0_const_y', ...
                        dataElements, isTargetToHost);
                end
            end
            
            % support for InitializeFunction outputs
            if this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot()
                writeSectionInitYDataProcessingCase(this, ...
                    this.codeInfo.InitializeFunctions, ...
                    'XIL_INITIALIZE_COMMAND', ...
                    'xil_fcnid0_init');
            end                                   
            
            % support for TerminateFunctions outputs
            if this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot() && this.HasModelWideEvents
                writeSectionInitYDataProcessingCase(this, ...
                    this.codeInfo.TerminateFunctions, ...
                    'XIL_TERMINATE_COMMAND', ...
                    'xil_fcnid0_term');
            end
            
            tableName = 'xil_fcnid0_task';
            tasks = this.getOutputTasks;
            commandIdxSwitchGeneratedForTasks = writeSectionInitYDataProcessingCaseWithCommandIdxSwitch(this, ...
                tasks, ...
                'XIL_OUTPUT_COMMAND', ...
                 tableName);
            
            callerFunctionsInfo = this.getCallerFunctionsInfo;
            commandIdxSwitchGeneratedForCallers = false;
            if ~isempty(callerFunctionsInfo)
                tableNamePostFix = '_output_u';
                numCallerFcns = length(callerFunctionsInfo);                
                for callerFcnIdx = 1:numCallerFcns
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                    commandIdxSwitchGeneratedForCallers =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numCallerFcns, ...
                        num2str(callerFunctionInfo.getTID), ...
                        'XIL_CLIENT_SERVER_COMMAND', ...
                        dataElements,...
                        tableName,...
                        tableNamePostFix,...
                        commandIdxSwitchGeneratedForCallers);
                end
                if commandIdxSwitchGeneratedForCallers
                    % generate default
                    this.writer.writeCellLines({'default:';
                        '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                        '   break;'});
                    % switch indent
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                    this.writer.writeLine('break;');
                    this.writer.decIndent;
                    this.writer.writeLine('}');
                end
            end

            commandIdxSwitchGenerated = commandIdxSwitchGeneratedForCallers ||...
                commandIdxSwitchGeneratedForTasks;
            
            if this.codeInfoUtils.isExportFunctions
                % support for Export Functions DisableFunction outputs
                %
                % map directly on to outputs for step command
                %
                this.writeSectionInitDataProcessingSwitchStart();
                this.writer.writeLine('case XIL_DISABLE_COMMAND: {');
                this.writer.incIndent;
                this.writer.wLine('MemUnit_T lResponseId = XIL_RESPONSE_OUTPUT_DATA;');
                this.writer.wLine('uint32_T lServerFcnId = 0;');
                this.writer.writeLine('errorCode = xilGetTargetToHostData(xilFcnId, XIL_OUTPUT_COMMAND, xilCommandIdx, xilIOData, lResponseId, lServerFcnId);');
                this.writer.writeLine('return errorCode;');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end
        
        function writeSectionInitYDataProcessingCase(this, ...
                                                     functionInterfaces, ...                                                                      
                                                     command, ...
                                                     tableName)                                                              
            isTargetToHost = true;
            uploadConstSigLogsAndSharedLocalDataStores = false;
            uploadInitializeIRTData = false;
            uploadTerminateIRTData = false;
            switch command
                case 'XIL_INITIALIZE_COMMAND'
                    uploadConstSigLogsAndSharedLocalDataStores = true;
                    uploadInitializeIRTData = this.HasModelWideEvents;
                case 'XIL_TERMINATE_COMMAND'
                    uploadTerminateIRTData = this.HasModelWideEvents;
                case {'XIL_SYSTEM_INITIALIZE_COMMAND', ...
                      'XIL_ENABLE_COMMAND', ...
                      'XIL_DISABLE_COMMAND', ...
                      'XIL_SYSTEM_RESET_COMMAND'}
                    % do nothing
                otherwise                    
                    assert(false, 'Unexpected command: %s', command);
            end
                                                 
            % process outports            
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, ...
                'outputsize');
            % process output size data prior to processing output data
            % so that the host's row major transform can access the current
            % sizes
            dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.codeInfoUtils.getDataInterfacesForFunction(functionInterfaces, 'output'), ...
                                'output')];
            if uploadConstSigLogsAndSharedLocalDataStores || uploadInitializeIRTData || uploadTerminateIRTData
                loggingData = this.SILPILWrapperUtils.getLoggingData;
                loggingDataIndices = [];
                if uploadConstSigLogsAndSharedLocalDataStores
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'))];
                end
                if uploadInitializeIRTData
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('INITIALIZE'))];
                end
                if uploadTerminateIRTData
                    loggingDataIndices = [loggingDataIndices ...
                        this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('TERMINATE'))];
                end
                selectedLoggingData = loggingData(loggingDataIndices);
                selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                end
            end
            if strcmp(command,'XIL_TERMINATE_COMMAND')
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getWriterSharedDataStoresForTask(termTask,'term'), 'dswrite'), 'dswrite')]; %Extra Argument needed to workaround G1411813
                end
            end
            if ismember(command, {'XIL_INITIALIZE_COMMAND', 'XIL_SYSTEM_INITIALIZE_COMMAND'})
                %Add Data Store Writes to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();                
                dsWriteIndices = [];
                for taskIdx = 1:length(initTask)
                    dsWriteIndices = [dsWriteIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getWriterSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dswrite')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsWriteIndices, 'stable'), 'dswrite')];
                if uploadConstSigLogsAndSharedLocalDataStores
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        1:length(this.codeInfoUtils.getSharedLocalDataStores), 'initializeSharedLocal'), 'sharedLocalDataStore')];
                end
            end
            
            if ~isempty(dataElements)
                this.writeSectionInitDataProcessingCase(command, ...
                    [tableName '_y'], ...
                    dataElements, isTargetToHost);
            end            
        end
        
        function commandIdxSwitchGenerated = writeSectionInitYDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks                
                task = tasks(taskIdx);
                               
                id = int2str(task.id);
                % process outports, dswrite and timer
                loggingData = this.SILPILWrapperUtils.getLoggingData;                

                % ports may not be part of the function signature
                portIndices = this.codeInfoUtils.getOutputPortsForTask(task);
                outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(portIndices);
                % process dsWrite for this task
                dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                    this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
                loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                loggingDataIndices = this.codeInfoUtils.getSignalLoggingIndices(loggingDataIndices);
                % process output size data prior to processing output data
                % so that the host's row major transform can access the current
                % sizes
                dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    outportSizesIndices, 'outputsize'), ...
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    portIndices, 'output'), ...                                
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                    dsWriteIndices, 'dswrite'),...
                                    this.getServerOutputArgsDataElement(task)];
                                
                selectedLoggingData = loggingData(loggingDataIndices);                                
                selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                end
                                                                                  
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        '_y',...
                        commandIdxSwitchGenerated);
                end
            end
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');   
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');                                                                                                                       
            end
        end
        
        function commandIdxSwitchGenerated = writeSectionInitSDataProcessingCaseWithCommandIdxSwitch(this, ...
                                                                                                     tasks, ...
                                                                                                     command, ...
                                                                                                     tableName)
            numTasks = length(tasks);             
            commandIdxSwitchGenerated = false;                                               
            for taskIdx = 1:numTasks                
                task = tasks(taskIdx);
                               
                id = int2str(task.id);
                % process states
                loggingData = this.SILPILWrapperUtils.getLoggingData;                
                loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                loggingDataIndices = this.codeInfoUtils.getStateLoggingIndices(loggingDataIndices);
                dataElements = [];                                
                selectedLoggingData = loggingData(loggingDataIndices);                                
                selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                for i=1:length(selectedLoggingData)
                    dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                end
                                                                                  
                if ~isempty(dataElements)
                    commandIdxSwitchGenerated =...
                        writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                        this, ...
                        numTasks, ...
                        id, ...
                        command, ...
                        dataElements,...
                        tableName,...
                        '_s',...
                        commandIdxSwitchGenerated);
                end
            end
            if commandIdxSwitchGenerated
                % generate default
                this.writer.writeCellLines({'default:';
                    '   errorCode = XIL_INTERFACE_UNKNOWN_TID;';
                    '   break;'});
                % switch indent
                this.writer.decIndent;
                this.writer.writeLine('}');   
                this.writer.writeLine('break;');
                this.writer.decIndent;
                this.writer.writeLine('}');                                                                                                                       
            end
        end
        
        function commandIdxSwitchGenerated =...
                writeSectionInitYDataProcessingCaseWithCommandIdxSwitchBody(...
                                                                            this, ...
                                                                            numTasks, ...
                                                                            taskId, ...
                                                                            command, ...
                                                                            dataElements,...
                                                                            tableName,...
                                                                            tableNamePostFix,...
                                                                            commandIdxSwitchGenerated)            
            if numTasks > 1
                if ~commandIdxSwitchGenerated
                    this.writeSectionInitDataProcessingSwitchStart();
                    this.writer.writeLine(['case ' command ': {']);
                    this.writer.incIndent;
                    % apply nested switch on "xilCommandIdx"
                    this.writer.writeLine('switch(xilCommandIdx) {');
                    % switch indent
                    this.writer.incIndent;
                    commandIdxSwitchGenerated = true;
                end
                caseId = taskId;
            else
                caseId = command;
            end
            isTargetToHost = true;
            this.writeSectionInitDataProcessingCase(caseId, ...
                [tableName taskId tableNamePostFix], ...
                dataElements, isTargetToHost);           
        end
        
        function writeSectionTerminate(this)
            
            this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilTerminate(uint32_T xilFcnId)')            
            this.writer.wBlockStart('if (xilFcnId != 0)');
            this.writer.wLine('return XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.wBlockEnd(); %if xilFcnId !=0
            
            this.writer.wComment('Invoke any terminate Function');
            this.writeSectionTerminateBody();
                        
            if this.AllocatesCppObject
                mdlObjectImplementation = this.getCPPModelObjectImpl();
                mdlObjectName = this.getExpression(mdlObjectImplementation);
                this.writer.writeLine('delete %s();', ...
                    this.getModelObjectPtrAccessorName(mdlObjectName));
            end
            
            this.writer.wLine('return XIL_INTERFACE_SUCCESS;');
            this.writer.wBlockEnd();            
        end
        
        function writeSectionInitTargetDataBody(this)
            if ~isempty(this.InitializeDataFcns)
                %Write out Opaque Region based Target data
                this.writer.newLine();
                this.writer.wComment('Initialize Opaque Region Based Data');
                this.writer.wLine(this.InitializeDataFcns);
            end
        end

        function writeSectionTerminateBody(this)
            
            termFunction = this.codeInfo.TerminateFunctions;
            if isempty(termFunction)
                % no terminate function
                callTerminateFunction = false;
            else
                % expect a single terminate function
                assert(isscalar(termFunction));
                % single terminate function
                callTerminateFunction = true;
            end
            
            if this.SILPILWrapperUtils.isMatFileLoggingEnabled                
                this.writeSectionTerminateMatFileLogging();
            end
            
            %
            isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
            if callTerminateFunction
                isSetBeforeInit = false;
                this.writeClassGetSetCalls('Set', isSetBeforeInit); %Must be final call before function is called.

                inputIndices = this.codeInfoUtils.getInputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'));
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    inputIndices, 'input');
                dataElements = [dataElements rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dsread')];
                this.writeInputAssignments(dataElements);

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

                outputIndices = this.codeInfoUtils.getOutputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'));
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    outputIndices, 'output');
                dataElements = [dataElements rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite')];
                this.writeOutputAssignments(dataElements);

                if isExecutionProfilingEnabled
                    this.writer.writeLine('%s(%dU);',...
                        instrCalls{2},...
                        sectionIdForFunction);
                end
                isSetBeforeInit = false;
                this.writeClassGetSetCalls('Get', isSetBeforeInit);  %Must be first code after function called
            else
                this.writeEmptyFunctionCall;                
            end
            this.writer.newLine;

            if this.SILPILWrapperUtils.AreEventCallbacksRequired
                this.writer.writeLine('TerminateEvent();');
                this.writer.newLine;
            end
            
            this.writer.wComment('Free Opaque Region Based Data');
            this.writer.wLine(this.FreeDataFcns);
        end
        
        function writeSectionEnable(this)                        
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilEnable(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});            
            this.writeSectionEnableBody;
            this.writer.decIndent;            
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionEnableBody(this)
            % no enable function
            this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            this.writer.writeLine('/* No Enable Function - this function should never be called */');
            this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
        end                
        
        function writeSectionDisable(this)            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilDisable(uint32_T xilFcnId, uint32_T xilTID) {');
            this.writer.incIndent;
            this.writer.writeCellLines({'/* Single In-the-Loop Component */';
                'if (xilFcnId != 0) {';
                '   return XIL_INTERFACE_UNKNOWN_FCNID;';
                '}';});
            this.writeSectionDisableBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;                        
        end
        
        function writeSectionDisableBody(this)
            % no disable function
            this.writer.writeLine('UNUSED_PARAMETER(xilTID);');
            this.writer.writeLine('/* No Disable Function - this function should never be called */');
            this.writer.writeLine('return XIL_INTERFACE_UNKNOWN_TID;');
        end
        
        % write the xilIOData table variable
        function writePILIODataTable(this, dataElements, tag)              
            this.writer.writeLine(['static XILIOData xil_fcnid0' tag '[' ...
                this.getNumxilIODataTableElements(dataElements) '];']);
            this.writer.newLine;
        end
        
        % write xilIOData tables for init functions
        function writeFuncXILIOData(this, funs, name)                          
            switch name
                case 'init'
                    uploadConstSigLogsAndSharedLocalDataStores = true;
                case {'system_initialize', 'term', 'enable', 'disable', 'system_reset'}
                    uploadConstSigLogsAndSharedLocalDataStores = false;
                otherwise                    
                    assert(false, 'Unexpected name: %s', name);
            end
            % process inports and params        
            inportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, ...
                                                                                 'inputsize');
            outportSizesIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, ...
                                                                                 'outputsize');
            inputIndices = this.codeInfoUtils.getDataInterfacesForFunction(funs, 'input');
            
            paramIndices = this.getParamIndicesForFunction(funs);
            taskCounterOffsetDataElements = rtw.pil.DataElement.empty;

            if strcmp(name,'init')
                inputIndices = this.codeInfoUtils.getAdditionalInitializeInportIndices(inputIndices);
                % write xilIOData table entries for InstP model arguments
                paramIndices = [paramIndices, this.SILPILWrapperUtils.getInstPArgumentIndices()];
                taskCounterOffsetDataElements = this.getTaskCounterOffsetDataElements();
            elseif strcmp(name,'term')
                inputIndices = unique([inputIndices, ...
                    this.codeInfoUtils.getInputPortsForRates(this.codeInfoUtils.getRates('TERMINATE'))]);
            end
            dataElements = [taskCounterOffsetDataElements, ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inputIndices, 'input'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, inportSizesIndices, 'inputsize'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, outportSizesIndices, 'outputsize'), ...
                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, paramIndices, 'parameter')];
            if ismember(name,{'init', 'system_initialize'}) 
                %Add Data Store reads to INITIALIZE call
                initTask = this.codeInfoUtils.getInitializeTasks();
                dsReadIndices = [];
                for taskIdx = 1:length(initTask)
                    dsReadIndices = [dsReadIndices, ...                         
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dsread')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                end
                dataElements = [dataElements, ...
                    rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                    unique(dsReadIndices, 'stable'), 'dsread')];                
            elseif strcmp(name,'term')
                %Add Data store Reads to TERMINATE Calls
                termTask = this.codeInfoUtils.getTerminateTasks();
                if ~isempty(termTask) && ...
                        this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                    dataElements = [dataElements, ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                        this.codeInfoUtils.getReaderSharedDataStoresForTask(termTask, 'term'), 'dsread'), 'dsread')]; %Extra Argument needed to workaround G1411813
                end
            end                
                            
                            
            if ~isempty(dataElements)                
                tag = ['_' name '_u'];
                this.writePILIODataTable(dataElements, tag);
            end
            % process outports
            if (this.SILPILWrapperUtils.getClientInterface.syncIODuringOneShot())                
                if (strcmp(name,'term') && this.HasModelWideEvents) || ~strcmp(name,'term')
                    % process output size data prior to processing output data
                    % so that the host's row major transform can access the current
                    % sizes
                    dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        outportSizesIndices, 'outputsize'), ...
                        rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                        this.codeInfoUtils.getDataInterfacesForFunction(funs, 'output'), 'output')];
                    if uploadConstSigLogsAndSharedLocalDataStores
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('ONESHOT'));
                        selectedLoggingData = loggingData(loggingDataIndices);
                        selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                        end
                    end
                    if strcmp(name,'term')
                        % Add logged signals in TERMINATE subsystem
                        if this.HasModelWideEvents
                            loggingData = this.SILPILWrapperUtils.getLoggingData;
                            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('TERMINATE'));
                            selectedLoggingData = loggingData(loggingDataIndices);
                            selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                            for i=1:length(selectedLoggingData)
                                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                            end
                        end
                        %Add Data store Writes to TERMINATE Calls                        
                        termTask = this.codeInfoUtils.getTerminateTasks();
                        if ~isempty(termTask) && ...
                                this.SILPILWrapperUtils.getSILPILInterface.getInTheLoopType~=rtw.pil.InTheLoopType.Block
                            dataElements = [dataElements, ...
                                rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                                this.codeInfoUtils.getWriterSharedDataStoresForTask(termTask, 'term'), 'dswrite'), 'dswrite')]; %Extra Argument needed to workaround G1411813
                        end
                    elseif ismember(name,{'init', 'system_initialize'})
                        % Add logged signals in INITIALIZE subsystem
                        if this.HasModelWideEvents
                            loggingData = this.SILPILWrapperUtils.getLoggingData;
                            loggingDataIndices = this.codeInfoUtils.getLoggingForRates(this.codeInfoUtils.getRates('INITIALIZE'));
                            selectedLoggingData = loggingData(loggingDataIndices);
                            selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                            for i=1:length(selectedLoggingData)
                                dataElements = [dataElements selectedLoggingData(i).getTargetDataElements]; %#ok<AGROW>
                            end
                        end
                        %Add Data Store Writes to INITIALIZE call
                        initTask = this.codeInfoUtils.getInitializeTasks();
                        
                        dsWriteIndices = [];
                        for taskIdx = 1:length(initTask)
                            dsWriteIndices = [dsWriteIndices, ...
                                this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                                this.codeInfoUtils.getWriterSharedDataStoresForTask(initTask(taskIdx), 'init'), 'dswrite')]; %#ok<AGROW> %Extra Argument needed to workaround G1411813
                        end
                        dataElements = [dataElements, ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            unique(dsWriteIndices, 'stable'), 'dswrite')];
                        if uploadConstSigLogsAndSharedLocalDataStores
                            dataElements = [dataElements, ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            1:length(this.codeInfoUtils.getSharedLocalDataStores), 'initializeSharedLocal'), 'sharedLocalDataStore')];
                        end
                    end
                    if ~isempty(dataElements)
                        tag = ['_' name '_y'];
                        this.writePILIODataTable(dataElements, tag);
                    end
                end                
            end
        end                                           
        
        function validateInterface(this)
            
            % validate I/O
            this.checkDataInterfacesAreAccessibleAndWritable(this.codeInfo.Inports, 'inport');
            this.checkDataInterfacesAreAccessibleAndWritable(this.codeInfo.Outports, 'outport');

            % validate I/O implementations
            this.checkIfImplementationsNeedToSyncInUpdate();
            this.checkIOPortsCanDefineStorage();

            %Validate Data Stores can be accessed
            allSharedDataStores = this.SILPILWrapperUtils.getDataStoresToTransferInterfaces();
            this.checkDataInterfacesAreAccessibleAndWritable(allSharedDataStores, 'datastore');
            
            % validate parameters
            dataInterfaces = this.codeInfo.Parameters;
            % preallocate to false
            this.IsImportedDialogParam = false(1, length(dataInterfaces));
            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);
                
                this.validateParamDataInterface(dataInterface);
                
                % store if the implementations cause a warning to be issued
                % or not
                this.IsImportedDialogParam(i) = this.importedNonTunableParam(dataInterface);
                
            end
            
            % validate that the generated code does not contain instance
            % specific parameters that SIL/PIL cannot support
            this.validateInstSpecificParameters;
        end

        function checkIOPortsCanDefineStorage(~)
            % no op by default
        end
       
        function checkIfImplementationsNeedToSyncInUpdate(~)
            %no op by default
        end
        
        function importedNonTunableParam = importedNonTunableParam(this, dataInterface)
            
            importedNonTunableParam = false;
            
            % 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 warning 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 warning 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.
                    
                    dataInterfaceImplementationDefined = this.isDefined(dataInterface.Implementation);
                    
                    if ~dataInterfaceImplementationDefined
                        diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, 'Parameter');
                        MSLDiagnostic('PIL:pil:ImportedNonTunableParameter', diString).reportAsWarning;
                        
                        importedNonTunableParam = true;

                    end
                    
                end
            end
        end
        
        function validateParamDataInterface(~, ~)
            % override in subclass
        end
        
        % write assignments from the data transfer variable to the
        % target variable
        function writeInputAssignments(this, dataElements)                                                                                                    
            for dataElementIdx = 1:length(dataElements)
                dataElement = dataElements(dataElementIdx);
                dataInterface = dataElement.getDataInterface;
                dataInterface.writeInputAssignment(dataElement, this.writer, this);
            end
        end                    
                        
        % write assignments from the target variable to the data transfer variable
        function writeOutputAssignments(this, dataElements)             
            for dataElementIdx = 1:length(dataElements)
                dataElement = dataElements(dataElementIdx);                                                                                  
                dataInterface = dataElement.getDataInterface;                                                                                
                dataInterface.writeOutputAssignment(dataElement, this.writer, this);                                                
            end
        end 
        
        % return the "base" storage implementation for a root implementation
        function storageImplementation = getBaseStorageImplementation(this, implementation)
            %Call shared implementation for getBaseStorageImplementation in
            %SILPILWrapperUtils.
            storageImplementation = this.InterfaceStrategy.getBaseStorageImplementation(implementation);
        end     
        
        function isDef = isDefined(this, implementation) 
            isDef = this.InterfaceStrategy.isDefined(implementation);
        end
        
        % template method to write required Defines for Matfile logging
        function writeSectionDefinesMatFileLogging(this) %#ok<MANU>
        end

        % template method to write code to update vars for Matfile logging
        function writeSectionOutputBodyMatFileLogging(this) %#ok<MANU>
        end

        % template method to write code to stop Matfile logging
        function writeSectionTerminateMatFileLogging(this) %#ok<MANU>
        end
        
        function modelObjectImpl = getCPPModelObjectImpl(this)
            assert(this.SILPILWrapperUtils.isCPPEncap, ...
                   'getCPPModelObjectImpl is only valid for C++ class generation');
            modelObjectImpl = this.getCodeInfo.getModelVariable();
        end
        
        function needToAllocate = needToAllocate(this, dataInterface, isBaseStorageDefined, isPointerVariable)
            % XIL needs to allocate data that is not defined
            needToAllocate = ~isBaseStorageDefined && ...
                this.needToAllocateHook(dataInterface, isPointerVariable);
        end
        
        function isPointerAllocd = needToAllocateHook(~, ~, ~)
            % default method
            isPointerAllocd = true;
        end
        
        % return output tasks, filtering server functions if needed
        function tasks = getOutputTasks(this)
            tasks = this.codeInfoUtils.getOutputTasks;
            keepIndices = true(1,length(tasks));
            for taskIdx = 1:length(tasks)
                [isServerFunction, serverFunctionInfo] =...
                    isServerOutputFunction(this.codeInfoUtils, ...
                        tasks(taskIdx).codeInfoData);
                if isServerFunction && ...
                        (~this.SILPILWrapperUtils.needToProcessServerFcns || ~serverFunctionInfo.IsExported)
                    % remove all server fcns if not needed and remove all
                    % non-exported (i.e., all local scoped) server fcns.
                    keepIndices(taskIdx) = false;
                end
            end
            tasks = tasks(keepIndices);
        end
        
        function validateBaseStorageImplementation(this, implementation, storageImplementation)
            if this.codeInfoUtils.isa(storageImplementation, 'CustomExpression')
                % make sure coder.descriptor.CustomExpression is not referenced
                % from e.g. an coder.descriptor.ArrayExpression or any other
                % parent implementation - it must be at the top-level
                assert(implementation == storageImplementation, ...
                    'Implementation & StorageImplementation must be equal for CustomExpression.');
                if rtw.pil.TargetInterface.isGetSetFunctionAccess(storageImplementation) || ...
                        this.codeInfoUtils.isa(storageImplementation, 'ClassMethodAccess')
                    storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
                    assert(~rtw.connectivity.CodeInfoTypeUtils.isComplex(storageType), ...
                        'Get/Set CSC cannot be complex.');
                    if implementation.AccessViaMacro
                        % error out for unsupported get/set functions with AccessViaMacro
                        DAStudio.error('PIL:pil:UnsupportedCSCGetSetViaMacro');
                    end
                elseif ~rtw.pil.TargetInterface.isGetSetCustomExpression(storageImplementation)
                    % Imported struct, bitfield, etc. should be encoded
                    % as CustomVariable, not CustomExpression.
                    assert(this.isDefined(storageImplementation), ...
                        'CustomExpression not supported for imported data scopes');
                end
            elseif this.codeInfoUtils.isa(storageImplementation, 'BasicAccessFunctionExpression')
                % error out for unsupported storage classes with data
                % access through macro
                ioAccessMode = storageImplementation.IOAccessMode;
                if ismember(ioAccessMode, {'ByMacro' 'BY_MACRO'})
                    DAStudio.error('PIL:pil:UnsupportedAccessViaMacro');
                end
            end
        end
        
        function typeIdentifier = getStorageTypeIdentifier(this, storageImplementation, needsReplaceType, isPointerToMatrix)
            % C storage type gives structtype for struct data and
            % complextype for complex data
            if isempty(storageImplementation)
                typeIdentifier = '';
                return;
            end
            % call getStorageTypeFromImplementation to get correct code
            % type for the implementation; This function will return
            % CodeType for stdContainers
            storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation ...
                (storageImplementation, true);
            cstorageType = rtw.connectivity.CodeInfoTypeUtils.getCStorageType(storageType);
            typeIdentifier = cstorageType.Identifier;
            if needsReplaceType
                typeIdentifier = this.codeInfoUtils.getReplacedTypeIdentifier(typeIdentifier);
            end
            if isPointerToMatrix
                % Modify type for pointer to matrix case
                % Ensure type is includes correct pointer representation.
                % NOTE: The type is only a pointer if the underlying data
                % is a matrix.
                typeIdentifier = sprintf('%s*', typeIdentifier);
            end
            % TODO g2923515: Remove once type replacement is available via
            % OpaqueRegion
            pluginManager = this.SILPILWrapperUtils.getSILPILInterface().getServicePluginManager();
            if matlab.internal.feature("XILMessageSupport") && ~isempty(pluginManager) && pluginManager.HasActiveServices && ...
                    this.MessageInterfaceClassMap.isKey(typeIdentifier)
                typeIdentifier = this.MessageInterfaceClassMap(typeIdentifier);
            end
        end       
        
        function [storage_size_str, storage_index] = getStorageSizeStrAndIndex(this, storageImplementation, lDimsPreserved)
            % Returns strings specifying the storage size and storage
            % index.
            numElements = this.getStorageNumElements(storageImplementation);
            lHasStorageImplementation = ~isempty(storageImplementation);
            if lHasStorageImplementation
                % call getStorageTypeFromImplementation to get correct code
                % type for the implementation with useCodeType = true
                storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
                isArray = rtw.connectivity.CodeInfoTypeUtils.isVector(storageType, ...
                  this.SILPILWrapperUtils.getClientInterface);
            else
                isArray = false;
            end
            isScalar = ~isArray;
            if isScalar
                storage_size_str = '';
                storage_index = '';
            else
                storage_index = '[0]';
                if lDimsPreserved
                    cType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation, lDimsPreserved);
                    symbolicDims = rtw.connectivity.CodeInfoTypeUtils.getSymbolicDimensions(cType);
                    storage_size_str = '';
                    storage_index = repmat(storage_index, 1, length(symbolicDims));
                    for i=1:length(symbolicDims)
                        storage_size_str = sprintf('%s[%s]', storage_size_str, symbolicDims{i});
                    end
                else
                    storage_size_str = ['[' numElements ']'];
                end
            end
        end
        
        
        function fullExpression = getDeclareExpression(this, storageImplementation, isPointerType)
            
            arguments
                this (1,1) rtw.pil.TargetInterface
                storageImplementation (1,1)
                isPointerType (1,1) logical = false;
            end
            
            % Returns expression for declaring storage variables.
            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(storageImplementation)
                fullExpression = storageImplementation.DeclareExpression;
            else
                needsReplaceType = true;
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerType);
                
                declarationSpecifier = 'extern ';
                
                lDimsPreserved = false; % Don't preserve symbolic dims when just declaring
                storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, lDimsPreserved);
                
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                
                dataExpression = this.getExpression(storageImplementation);
                
                fullExpression = [declarationSpecifier storageVolatile typeIdentifier ' ' ...
                    dataExpression ...
                    storage_size_str ';'];
            end
        end
        
        
        function fullExpression = getDefineExpression(this, storageImplementation, xilDataInterface)
            % GETDEFINEEXPRESSION
            % Returns expression for defining storage variables.
            implementation = xilDataInterface.getRTWDataInterfaceImplementation();
            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(storageImplementation)
                fullExpression = storageImplementation.DefineExpression;
            elseif rtw.connectivity.CodeInfoUtils.canGetDefineDataFromOpaqueRegion(implementation)
                % get the define data from the opaque region
                fullExpression = implementation.OpaqueRegion.DefineDataFunction.getFunctionCall( ...
                    xilDataInterface.getRTWDataInterface());
                if ~isequal(storageImplementation, implementation)
                    % add any define data from the storageImplementation
                    % (e.g. the implementation.BaseRegion) as it is not
                    % included in the implementation.OpaqueRegion
                    fullExpression = [fullExpression, ...
                        this.getDefineExpressionFromImplementation(storageImplementation, xilDataInterface)];
                end
            else
                fullExpression = this.getDefineExpressionFromImplementation(storageImplementation, xilDataInterface);
            end
        end

        function shouldUseMdlRefDWork = shouldUseMdlRefDWorkRtmAsTargetVar(this, dataInterface)
           shouldUseMdlRefDWork = (this.SILPILWrapperUtils.isRTMPointer(dataInterface) && this.shouldReplaceRTMPointerVar() && ~isempty(this.codeInfoUtils.MdlRefDWorkInternalData));
        end

        function dataExpression = updateDataExpressionWithModelRefDWork(this, dataExpression, dataInterface, storageImplementation)
            if ~this.shouldUseMdlRefDWorkRtmAsTargetVar(dataInterface)
                return;
            end
            MdlRefDWorkImpl = this.codeInfoUtils.MdlRefDWorkInternalData.Implementation;
            mdlRefDWType = MdlRefDWorkImpl.Type;
            elements = mdlRefDWType.Elements;
            for tIdx = 1:length(elements)
                if strcmp(elements(tIdx).Type.Identifier, storageImplementation.Type.Identifier)
                    dataExpression = ['(' MdlRefDWorkImpl.Identifier '.' elements(tIdx).Identifier ')'];
                    return
                end
            end
            assert(false, 'Could not find a replacement expression for target variable.');
        end


        function fullExpression = getPointerDefineExpression(this, implementation, dataInterface, storageImplementation, lDimsPreserved, needToAllocate, isPointerToMatrix)
            % Returns expression for declaring/initializing pointer
            % variables for storage.
            [~, storage_index] = this.getStorageSizeStrAndIndex(...
                storageImplementation, lDimsPreserved);
            storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
            dataExpression = this.getExpression(storageImplementation);
            dataExpressionPtr = this.getExpression(implementation);
            
            needsReplaceType = true;
            typeIdentifier = this.getStorageTypeIdentifier( ...
                                    storageImplementation, ...
                                    needsReplaceType, ...
                                    isPointerToMatrix);

            dataExpression = this.updateDataExpressionWithModelRefDWork(dataExpression, dataInterface, storageImplementation);


            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(storageImplementation)
                % isPointerVariable is derived based on the class of the
                % implementation object.  For CSCs of type "other",
                % getPointerDefineExpression should not be used.
                assert(false, ...
                    'CSCs of type "Other" with pointer access are not represented by PointerVariable');
            else
                if needToAllocate
                    %Pointer variable needs initializing to point
                    %storage item declared above.
                    fullExpression = [storageVolatile typeIdentifier ' * ' ...
                        dataExpressionPtr ' = &' ...
                        dataExpression ...
                        storage_index ';'];
                else
                    %Allocation Function will dynamically allocate this
                    %variable, so just declare it.
                    fullExpression = sprintf('%s %s * %s;', ...
                        storageVolatile, typeIdentifier, dataExpressionPtr);
                end
            end
        end
        
        function fullExpression = getGroupedDefineExpression(this, implementation)
            assert(this.codeInfoUtils.isa(implementation, 'CustomVariable'));
            fullExpression = strtrim(implementation.ClassAccess.GroupDataDefinition);
        end
        
        function initExpression = getInitializeExpression(this, implementation, cData, indices, initString)
            % Returns expression for initializing storage implementation
            %
            % Currently just calls getWriteExpression and writes what the
            % caller passes as initString.  For CSCs of type "Other" in
            % theory we could use the response to the "initialize" request
            % returned by the TLC, but in this case, the "ground" value
            % might be a variable that is not defined in the generated
            % code if we are using a model ref code interface, or have
            % assigned ownership of the variable elsewhere.
            initExpression = this.getWriteExpression(implementation, cData, indices, initString);
        end
        
        % template method to write additional storage allocation
        function writeSectionStorageHook(~)
        end
        
        % template method to write RTW pointer assignment before calling
        % into the init_ function
        function writeRTMPointerAssignmentHook(this)
            assignment = this.InterfaceStrategy.getRTMPointerAssignment();
            this.writer.writeLine('%s', assignment);
        end
        
        function isObj = isCPPModelObject(this, storageImplementation)
            isObj = this.SILPILWrapperUtils.isCPPEncap && ...
                (storageImplementation == this.getCPPModelObjectImpl());
        end
        
        function validateInstSpecificParameters(~)
            % default implementation
        end

        function writeModelObjectAccessFunction(this, modelObjName, modelObjType)
            % Use an accessor function to control
            % initialization of the model object.   This is
            % essential to support LDRA code coverage which
            % calls targetFopen during execution of
            % the rtObject the constructor. We must delay this
            % until after xilInit to a point where the host
            % s-function is ready to accept the incoming data.
            % Top-tester for LDRA is
            % codecoverage/sharedtests/tcodecov_basic_topModel.m:simple_top_model_sil_cpp_encap
            this.AllocatesCppObject = true;
            modelObjPtrAccessor = this.getModelObjectPtrAccessorName(modelObjName);
            this.writer.wComment('Map model object name to dereferenced pointer to model object');
            this.writer.wLine('#define %s (*%s())', modelObjName, modelObjPtrAccessor);
            this.writer.newLine;

            this.writer.wComment('Accessor function to control initialization of model object');
            this.writer.wBlockStart('%s * %s(void)', ...
                modelObjType, ...
                modelObjPtrAccessor);
            % call the constructor function to instantiate the obj
            ctor = this.codeInfo.ConstructorFunction;
            assert(~isempty(ctor), 'ConstructorFunction should not be empty');
            ctorCall = ctor.getFunctionCall;
            objPtr = ['xil_' modelObjName '_ptr'];
            this.writer.wLine('static %s* %s = new %s;', modelObjType, objPtr, ctorCall);
            this.writer.wLine('return %s;', objPtr);
            this.writer.wBlockEnd;
        end
    end
   
    methods (Access = 'private')    
        
        function lMethod = processOpaqueRegionMethod(this, lRawMethod, lImplementation)
            lMethod = lRawMethod;
            if this.codeInfoUtils.isa(lImplementation, 'ClassMethodExpression') && ...
                    contains(lMethod, lImplementation.getExpression)
                % with getter/setter methods, the local variable must
                % be used for init/term and serialization/de-serialization
                % instead of the getter/setter
                newExpr = this.getExpression(lImplementation);
                lMethod = strrep(lMethod, [lImplementation.getExpression '()'], newExpr);
                lMethod = strrep(lMethod, lImplementation.getExpression, newExpr);
            end
            if rtw.connectivity.CodeInfoUtils.isCoderArrayType(lImplementation.CodeType)
                % workaround issue with coder::array arguments in ser/deser
                newExpr = this.getExpression(lImplementation);
                lMethod = strrep(lMethod, ['&(' newExpr ')'], newExpr);
            end
        end

        function [nInstances, nElements] = writeStorageDefinition(this, dataElement, dataType, identifier)
            nInstances = dataElement.getNumInstances;
            nElements = dataElement.getNumElements;

            if dataElement.getDataInterface.HasTargetSideOpaqueRegion
                % OpaqueRegions are responsible for defining their own
                % storage.
                return;
            end

            if nInstances == 1
                cDataInstancesStr = '';
            else
                cDataInstancesStr = ['[' dataElement.getSymbolicNumInstances ']'];
            end
            
            if nElements == 1
                storage_size_str = '';
            else
                storage_size_str = ['[' dataElement.getSymbolicNumElements ']'];
            end
            
            this.writer.wComment('Storage for assignment data transfer');
            this.writer.writeLine(['static ' dataType ' ' ...
                identifier ...
                cDataInstancesStr ...
                storage_size_str ';']);
        end
        
        function dataElements = getServerInputArgsDataElement(this, task)
            % get data elements for the server function input args            
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, task.codeInfoData);
            if isServerFunction
                assert(this.SILPILWrapperUtils.needToProcessServerFcns, ...
                    'Expect needToProcessServerFcns in getServerInputArgsDataElement');
                dataElements = serverFunctionInfo.getInputArgsDataElements;
            else
                dataElements = rtw.pil.DataElement.empty;
            end
        end
        
        function dataElements = getServerOutputArgsDataElement(this, task)
            % get data elements for the server function output args            
            [isServerFunction, serverFunctionInfo] =...
                isServerOutputFunction(this.codeInfoUtils, task.codeInfoData);
            if isServerFunction
                assert(this.SILPILWrapperUtils.needToProcessServerFcns, ...
                    'Expect needToProcessServerFcns in getServerOutputArgsDataElement');
                dataElements = serverFunctionInfo.getOutputArgsDataElements;
            else 
                dataElements = rtw.pil.DataElement.empty;
            end
        end
        
        function checkDataInterfacesAreAccessibleAndWritable(this, dataInterfaces, dataInterfaceType)
            for i=1:length(dataInterfaces)
                dataInterface = dataInterfaces(i);
                if strcmp(dataInterfaceType, 'inport') || ~this.codeInfoUtils.isa(dataInterface.Implementation, 'ClassMethodExpression')
                    this.checkIOReadOnly(dataInterface, dataInterfaceType);
                end
                % check ports are accessible (not static)
                %
                % e.g.
                % - In-the-Loop must be able to access Inports and Outports
                isStatic = this.codeInfoUtils.isDataInterfaceStatic(dataInterface);
                if isStatic
                    diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, dataInterfaceType);                                         
                    rtw.pil.ProductInfo.error('pil', ...
                                              'StaticDataInterface', ...
                                              diString);                    
                end
            end
        end
        
        function dataElements = getCallerFunctionTargetToHostDataElements(this, callerFunctionInfo)
            % process in args
            inArgsDataElements = callerFunctionInfo.getInputArgsDataElements;
            
            % process dsWrite
            task = [];
            task.codeInfoData = callerFunctionInfo.getCodeInfoData;
            dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
            dsWriteDataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                dsWriteIndices, 'dswrite');
            dataElements = [inArgsDataElements dsWriteDataElements];
        end
        
        function dataElements = getCallerFunctionHostToTargetDataElements(this, callerFunctionInfo)
            % process out args
            outArgsDataElements = callerFunctionInfo.getOutputArgsDataElements;
            
            % process dsread
            task = [];
            task.codeInfoData = callerFunctionInfo.getCodeInfoData;
            dsReadIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
            dsReadDataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                dsReadIndices, 'dsread');
            
            dataElements = [outArgsDataElements dsReadDataElements];
        end                
        
        % return the number of xilIOData table elements for the given
        % dataInterfaces
        function symbNumElements = getNumxilIODataTableElements(this, dataElements)  %#ok<INUSL>
            % always at least the final xilIOData
            numElements = 1;
            symbNumElements = '';
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);                
                % entry for each instances of the contiguous chunk
                if Inf == cData.getNumInstances
                    if isempty(symbNumElements)
                        symbNumElements = cData.getSymbolicNumInstances;
                    else
                        symbNumElements = sprintf( ...
                            '%s + %s', ...
                            symbNumElements,  cData.getSymbolicNumInstances);
                    end
                else
                    numElements = numElements + cData.getNumInstances;
                end                
            end
            if isempty(symbNumElements)
                symbNumElements = int2str(numElements);
            else
                symbNumElements = sprintf( ...
                    '%s + %d', symbNumElements, numElements);
            end
        end                                                     
        
        % write dynamic xilIOData table array initialization
        function writePILIODataTableInitialization(this, dataElements, tableName)
            this.writer.writeLine('static int initComplete = 0;');
            this.writer.wBlockStart('if (!initComplete)');
            this.writer.writeLine('uint32_T tableIdx = 0;');
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);
                
                if ~cData.shouldSkipTargetIOTableEntry
                    % open any required loops
                    cData.writeStructArrayHeader(this.writer);
                    % body
                    cData.getDataInterface.writePILIODataTableEntry(cData, tableName, this.writer, this);
                    % close any required loops
                    cData.writeStructArrayTrailer(this.writer);
                end
            end

            % final xilIOData - null pointer
            this.writer.writeLine([tableName '[tableIdx].memUnitLength = 0;']);
            this.writer.writeLine([tableName '[tableIdx++].address = (MemUnit_T *) 0;']);
            this.writer.writeLine('initComplete = 1;');
            this.writer.wBlockEnd(''); %if (!initComplete)
        end
        
        function writeTransferCalls(this, dataElements, isTargetToHost)
            if isTargetToHost
                this.writeResponseOutputData();
            end
            processedInterfaces = {};
            for idx=1:numel(dataElements)
                
                if dataElements(idx).getDataInterface.HasTargetSideOpaqueRegion
                    dataInterface = dataElements(idx).getDataInterface.getRTWDataInterface();
                    interfaceImplementation = rtw.connectivity.CodeInfoUtils.getImplElementsIfCollection(dataElements(idx).getDataInterface.getRTWDataInterfaceImplementation());

                    if ~any(cellfun(@(x)isequal(x,dataElements(idx).getDataInterface),processedInterfaces))
                        processedInterfaces{end+1} = dataElements(idx).getDataInterface; %#ok<AGROW>
                        opaqueRegion = interfaceImplementation.OpaqueRegion;

                        if isTargetToHost
                            lRawMethod = opaqueRegion.ReadFromData.getFunctionCall(dataInterface);
                        else
                            lRawMethod = opaqueRegion.WriteToData.getFunctionCall(dataInterface);
                        end
                        lMethod = this.processOpaqueRegionMethod(lRawMethod, interfaceImplementation);
                        this.writer.wLine('%s',lMethod);
                    end
                end
            end
        end
        function writeResponseOutputData(this)
            %Write out calls to send command information headers before
            %sending back output data.            
            this.writer.wBlockStart('');                        
            % Need to send the response id before sending back data
            this.writer.wBlockStart('if(xilWriteData(&responseId, sizeof(responseId)) != XIL_DATA_STREAM_SUCCESS)');
            this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
            this.writer.wBlockEnd(); %If send response Id.
            
            this.writer.wBlockStart('if(responseId == XIL_RESPONSE_CS_REQUEST_SERVICE)')
            this.writer.wBlockStart('if(xilWriteData((MemUnit_T *) &serverFcnId, sizeof(serverFcnId)) != XIL_DATA_STREAM_SUCCESS)');
            this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
            this.writer.wBlockEnd(); %If send server function.
            this.writer.wBlockEnd(); %If response is XIL_RESPONSE_CS_REQUEST_SERVICE.
            this.writer.wBlockEnd(); %Code Block
        end
        % helper function to process xilIOData
        function writeIOxilIOData(this, type, iotag)            
            %
            switch (type)
                case {'Outputs','mdlOutputsInputs', 'States'}
                    tasks = this.getOutputTasks;
                case 'mdlUpdateInputs'
                    tasks = this.codeInfoUtils.getUpdateTasks;
                otherwise
                    assert(false, 'Unexpected I/O type during target generation: %s\n', type);
            end
            
            for taskIdx = 1:length(tasks)
                task = tasks(taskIdx);                                
                
                % used for both inputs & outputs
                outportIndices = this.codeInfoUtils.getOutputPortsForTask(task);                                
                
                switch (type)
                    case 'mdlOutputsInputs'
                        
                        % process inports, params and dsread
                        inportIndices = this.codeInfoUtils.getInputPortsForTask(task);
                        inportIndices = this.SILPILWrapperUtils.getDFInputPortsIndices(inportIndices);
                        
                        
                        inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(inportIndices);
                        outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                        dsReadIndices =  this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            this.codeInfoUtils.getReaderSharedDataStoresForTask(task), 'dsread');
                                                
                        % process any additional data interfaces
                        internalDataInterfaceIndices = ...
                            this.TransferInterfaceUtils.getInternalDataToUpload(this.codeInfo, task.codeInfoData.Timing);
                        
                        %Note: Internal data must be first in the list as it's only
                        %processed via Opaque Regions. So is immediately de-serialized.
                        dataElements = [this.TransferInterfaceUtils.getDataInterfaces(this.SILPILWrapperUtils, ...
                            internalDataInterfaceIndices, 'internal'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportSizesIndices, 'inputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            this.getParamIndicesForFunction(task.codeInfoData), 'parameter'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            dsReadIndices, 'dsread'),...
                            this.getServerInputArgsDataElement(task)];
                    case 'mdlUpdateInputs'
                        inportIndices = this.codeInfoUtils.getInputPortsForTask(task);
                        inportIndices  = this.SILPILWrapperUtils.getNonDFInputPortsIndices(inportIndices);
                        inportSizesIndices = this.codeInfoUtils.getInportSizesIndices(inportIndices);
                        dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportIndices, 'input'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            inportSizesIndices, 'inputsize')];
                    case 'Outputs'
                        % process outports, dswrite and timer
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                        loggingDataIndices = this.codeInfoUtils.getSignalLoggingIndices(loggingDataIndices);
                        outportSizesIndices = this.codeInfoUtils.getOutportSizesIndices(outportIndices);
                        dsWriteIndices = this.SILPILWrapperUtils.getDataStoresToTransferIndices( ...
                            this.codeInfoUtils.getWriterSharedDataStoresForTask(task), 'dswrite');
                        % process output size data prior to processing output data
                        % so that the host's row major transform can access the current
                        % sizes
                        dataElements = [rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportSizesIndices, 'outputsize'), ...
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            outportIndices, 'output'), ...                            
                            rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                            dsWriteIndices, 'dswrite'),...
                            this.getServerOutputArgsDataElement(task)];
                        selectedLoggingData = loggingData(loggingDataIndices);
                        selectedLoggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(selectedLoggingData);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                        end
                    case 'States'
                        % process states
                        loggingData = this.SILPILWrapperUtils.getLoggingData;
                        loggingDataIndices = this.codeInfoUtils.getLoggingForTask(task);
                        loggingDataIndices = this.codeInfoUtils.getStateLoggingIndices(loggingDataIndices);
                        dataElements = [];
                        selectedLoggingData = loggingData(loggingDataIndices);
                        for i=1:length(selectedLoggingData)
                            dataElements = [dataElements selectedLoggingData(i).getTargetDataElements];                          %#ok<AGROW>
                        end
                    otherwise
                        assert(false, 'Unexpected I/O type during target generation: %s\n', type);
                end
                if isempty(dataElements)
                    % no xilIOData table required
                    continue;
                end
                tag = ['_task' int2str(task.id) '_' lower(iotag)];
                this.writePILIODataTable(dataElements, tag);
            end
            if ~strcmp(type,'mdlUpdateInputs') && ~strcmp(type,'States')
                % not done for States. Check if any action is required
                callerFunctionsInfo = this.getCallerFunctionsInfo;
                for callerFcnIdx = 1:length(callerFunctionsInfo)
                    callerFunctionInfo = callerFunctionsInfo(callerFcnIdx);
                    switch(type)
                        case 'mdlOutputsInputs'
                            dataElements = this.getCallerFunctionTargetToHostDataElements(callerFunctionInfo);
                        case 'Outputs'
                            dataElements = this.getCallerFunctionHostToTargetDataElements(callerFunctionInfo);
                        otherwise
                            assert(false, 'Unexpected I/O type during target generation: %s\n', type);
                    end
                    tag = ['_task' int2str(callerFunctionInfo.getTID) '_' lower(iotag)];
                    this.writePILIODataTable(dataElements, tag);
                end
            end
        end
        
        % write xilIOData table for constant outputs
        function writeConstOutputxilIOData(this)
            if this.codeInfoUtils.hasConstOutput
                dataElements = rtw.pil.CodeInfoData.createTargetDataElements(this.SILPILWrapperUtils, ...
                                                             this.codeInfoUtils.getOutputPortsForRates(this.codeInfoUtils.getRates('ONESHOT')), ...
                                                             'output'); 
                if ~isempty(dataElements)                                     
                    tag = '_const_y';
                    this.writePILIODataTable(dataElements, tag);
                end
            end
        end   
        
        % return the set of dataInterfaces to process for Get/Set function 
        % header files and definitions
        function dataInterfaces = getDataInterfacesForGetSetFunctionProcessing(this)
            allSharedDataStores = this.codeInfoUtils.getAllSharedDataStores;
            dataInterfaces = [this.codeInfo.Inports, ...
                              this.codeInfo.Outports, ...
                              allSharedDataStores, ...
                              this.codeInfo.Parameters(this.getParamIndicesForStorage), ...
                              this.getDataInterfacesForGetSetFunctionProcessingHook()];
        end

        function writeSectionStartupSimulinkVariantFunctions(this)
            simFcnInfos = this.getCodeInfo.StartupSimulinkVariantFcnInfos;
            for aIdx = 1 : length(simFcnInfos)
                simVarFcnInfo = simFcnInfos(aIdx);
                simVarFcnName = simVarFcnInfo.Name;
                simVarFcnCode = simVarFcnInfo.Code;
                this.writer.wBlockStart('boolean_T %s(void)', ...
                                simVarFcnName);
                this.writer.wLine('%s', simVarFcnCode);
                this.writer.wBlockEnd;
            end
        end
        
        function writeSectionGetSetFunctions(this)
            % define imported get / set functions referenced 
            % by the component under test                        
            dataInterfaces = this.getDataInterfacesForGetSetFunctionProcessing();
            headerWritten = false;
            processedSetFuns = {};
            processedGetFuns = {};
            for dataInterfaceIdx = 1:length(dataInterfaces)
                dataInterface = dataInterfaces(dataInterfaceIdx);
                implementation = dataInterface.Implementation;
                if rtw.pil.TargetInterface.isGetSetFunctionAccess(implementation)
                    if ~headerWritten
                        this.writer.wLine('/* Get/Set Functions */');
                        headerWritten = true;
                    end
                    
                    storageImplementation = this.getBaseStorageImplementation(implementation);
                    this.validateBaseStorageImplementation(implementation, storageImplementation);
                    
                    needsReplaceType = false;
                    isPointerToMatrix = false;
                    typeIdentifier = this.getStorageTypeIdentifier(storageImplementation, needsReplaceType, isPointerToMatrix);
                    numElements = this.getStorageNumElements(storageImplementation);
                    
                    setFunction = implementation.WriteExpression;

                    % filter duplicates
                    if ismember(setFunction, processedSetFuns) || isempty(setFunction)
                        defineSetFunction = false;
                    else
                        defineSetFunction = true;
                        processedSetFuns{end+1} = setFunction; %#ok<AGROW>
                    end    
                    getFunction = implementation.ReadExpression;

                    % filter duplicates
                    dataExpression = this.getExpression(storageImplementation);
                    if ismember(getFunction, processedGetFuns) || isempty(getFunction)
                        defineGetFunction = false; 
                    else
                        defineGetFunction = true;
                        processedGetFuns{end+1} = getFunction; %#ok<AGROW>
                    end
                    if strcmp(numElements,'1')
                        if defineSetFunction
                            this.writer.wBlockStart('void %s(%s data)', ...
                                setFunction, typeIdentifier);
                            this.writer.wLine('%s = data;', dataExpression );
                            this.writer.wBlockEnd;
                        end                     
                        if defineGetFunction
                            this.writer.wBlockStart('%s %s(void)', ...
                                typeIdentifier, getFunction);
                            this.writer.wLine('return %s;', dataExpression );
                            this.writer.wBlockEnd;
                        end
                    else
                        if defineSetFunction
                            this.writer.wBlockStart('void %s(size_t idx, %s data)', ...
                                setFunction, typeIdentifier);
                            this.writer.wLine('%s[idx] = data;', dataExpression );
                            this.writer.wBlockEnd;
                        end
                        if defineGetFunction
                            this.writer.wBlockStart('%s %s(size_t idx)', ...
                                typeIdentifier, getFunction);
                            this.writer.wLine('return %s[idx];', dataExpression );
                            this.writer.wBlockEnd;
                        end
                    end    
                    if defineSetFunction || defineGetFunction
                        this.writer.newLine;
                    end
                end                
            end
            this.writer.newLine;
        end
        
        function dataInterfaces = getDataInterfacesForDynamicAccessFunctions(this)
            
            %Note: Internal data must be first in the list as it's only
            %processed via Opaque Regions. So is immediately de-serialized.
            dataInterfaces = [rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.InternalData), 'internal'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.InterfaceStrategy.getInportIndicesForStorage(), 'input'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfo.Outports), 'output'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                this.getParamIndicesForStorage, 'parameter'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getWriterSharedDataStores), 'dswrite'), ...
                rtw.pil.CodeInfoData.createDataInterfaces(this.SILPILWrapperUtils, ...
                1:length(this.codeInfoUtils.getSharedLocalDataStores), 'sharedLocalDataStore'), ...
                this.getDataInterfacesForDynamicAccessFunctionsHook()];   
        end
        
        function writeSectionDynamicAccessFunctions(this)
            %Check I/O interfaces for dynamic regions. Define any get/set
            %functions attached to those regions.
            
            dataInterfaces = this.getDataInterfacesForDynamicAccessFunctions();
            loggingData = this.SILPILWrapperUtils.getLoggingData;
            loggingData = rtw.pil.LoggingUtils.filterUnneededBufferUpload(loggingData);
            dataInterfaces = [dataInterfaces loggingData];
            
            definedFunctions = {}; %Workaround for isDefined not working see g1774074.
            haveDefinedTargetSerializations = false;
            %Keep a list of functions we have defined and only define
            %functions that are different
            for idx=1:numel(dataInterfaces)
                implemenation = dataInterfaces(idx).getRTWDataInterface.Implementation;
                implementationList = this.codeInfoUtils.getImplElementsIfCollection(implemenation);
                for implIdx = 1:numel(implementationList)
                    implementation = implementationList(implIdx);
                    if rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation) 
                        if ~haveDefinedTargetSerializations
                            this.writer.wLine(this.SILPILWrapperUtils.getCodeInfo.XILTargetSerializationFunctionDefinitions);
                            haveDefinedTargetSerializations = true;
                        end
                    elseif ~isempty(implementation.DynamicMemory)
                        dynMem = implementation.DynamicMemory;
                        %Define any functions we previously haven't defined
                        
                        getters = dynMem.Getters.toArray;

                        for kGetter = 1:numel(getters)
                            %workaround method naming issue
                            currGetter = getters(kGetter);
                            isDefinedMethod = getIsDefinedMethodName(currGetter);
                            if ~currGetter.(isDefinedMethod) && ~ismember(currGetter.Name, definedFunctions)
                                this.writer.wLine(dynMem.getGetterFunctionBody(kGetter-1));
                                currGetter.(isDefinedMethod) = true;
                                definedFunctions{end+1} = currGetter.Name; %#ok<AGROW>
                            end
                        end

                        setters = dynMem.Setters.toArray;
                        for kSetter = 1:numel(setters)
                            %workaround method naming issue
                            currSetter = setters(kSetter);
                            isDefinedMethod = getIsDefinedMethodName(currSetter);
                            if ~currSetter.(isDefinedMethod) && ~ismember(currSetter.Name, definedFunctions)
                                this.writer.wLine(dynMem.getSetterFunctionBody(kSetter-1));
                                currSetter.(isDefinedMethod) = true;
                                definedFunctions{end+1} = currSetter.Name; %#ok<AGROW>
                            end
                        end
                    end
                end
            end
            
            function name = getIsDefinedMethodName(obj)
                if isprop(obj, 'IsDefined')
                    name = 'IsDefined';
                elseif isprop(obj, 'isDefined')
                    name = 'isDefined';
                else
                    assert(false, 'no property isDefined/IsDefined');
                end
            end
        end
        
        function writeSectionResponseTypeSize(this, hostDataType)
            allIODataTypes = this.SILPILWrapperUtils.getAllIODataTypes;
            if ismember(hostDataType, allIODataTypes)   
                % for targetDataType use native C datatypes to avoid any 
                % possible issues with data type replacement
                %
                % support PIL_FLOAT_SIZE and PIL_DOUBLE_SIZE for testing
                switch hostDataType
                    case 'single'
                        targetDefine = 'PIL_FLOAT_SIZE';
                        targetDataType = 'float';
                    case 'double'
                        targetDefine = 'PIL_DOUBLE_SIZE';
                        targetDataType = 'double';
                    otherwise
                        assert(false, 'Unexpected hostDataType: %s\n', hostDataType);
                end
                
                memUnitData = int2str(this.SILPILWrapperUtils.getTypeUtils.builtinTypeToTypeId(hostDataType));
                
                
                this.writer.writeLine('{');
                this.writer.incIndent;
                this.writer.writeCellLines({'/* send response id code */';
                    'MemUnit_T memUnitData = XIL_RESPONSE_TYPE_SIZE;';
                    'if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {'});
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeCellLines({'/* send type id */';
                    ['memUnitData = ' memUnitData ';'];
                    'if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {'});
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.writeLine(['/* ' targetDefine ' should only be already defined for MathWorks testing */']);
                this.writer.writeLine(['#ifndef ' targetDefine]);
                this.writer.incIndent;
                this.writer.writeLine(['#define ' targetDefine ' sizeof(' targetDataType ')']);
                this.writer.decIndent;
                this.writer.writeLine('#endif');                
                this.writer.writeCellLines({'/* send size in bytes */';
                    ['memUnitData = (MemUnit_T) ' targetDefine ';'];
                     '#ifndef HOST_WORD_ADDRESSABLE_TESTING';
                     '/* convert MemUnits to bytes */';
                     'memUnitData *= MEM_UNIT_BYTES;';
                     '#endif'});                                
                this.writer.writeLine('if (xilWriteData(&memUnitData, sizeof(memUnitData)) != XIL_DATA_STREAM_SUCCESS) {');
                this.writer.incIndent;
                this.writer.writeLine('return XIL_INTERFACE_COMMS_FAILURE;');
                this.writer.decIndent;
                this.writer.writeLine('}');
                this.writer.decIndent;
                this.writer.writeLine('}');
            end
        end
        
        % Write all cached Get or Set calls.
        function writeClassGetSetCalls(this, getOrSet, isSetBeforeInit)
            allKeys = this.DefinedDataList.keys;
            
            if ~isempty(allKeys)
                this.writer.wComment(sprintf('Call all %s class methods', getOrSet));
            end
            for idx = 1 : this.DefinedDataList.length
                curValue = this.DefinedDataList(allKeys{idx});
                if ~isempty(curValue) && ~isempty(curValue.(getOrSet)) && ...
                        ~(isSetBeforeInit && curValue.IsParameterInterface)
                    this.writer.wLine('%s;',curValue.(getOrSet));
                end
            end
            if ~isempty(allKeys)
                this.writer.newLine;
            end

        end
        
        function writeSectionSystemInitialize(this)            
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilSystemInitialize(uint32_T xilFcnId) {');                            
            this.writer.incIndent;
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionSystemInitializeBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end  
        
        function writeSectionSystemInitializeBody(this)                        
            this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                'if (xilFcnId == 0) {'});
            this.writer.incIndent;                                            
            
            % call SystemInitialize if applicable
            this.writeSectionSystemInitializeFunction;
            
            this.writeSectionSystemInitializeInitStates();
            
            this.writer.decIndent;
            this.writer.writeCellLines({'}' ...
                                        'else {'});
            this.writer.incIndent;
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            % add optional trailer
            this.writeSectionSystemInitializeTrailer;            
            this.writer.writeLine('return errorCode;');
        end   
        
        function writeSectionSystemReset(this)                        
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE xilSystemReset(uint32_T xilFcnId) {');
            this.writer.incIndent;
            this.writer.writeLine('XIL_INTERFACE_ERROR_CODE errorCode = XIL_INTERFACE_SUCCESS;');
            this.writeSectionSystemResetBody;
            this.writer.decIndent;
            this.writer.writeLine('}');
            this.writer.newLine;
        end
        
        function writeSectionSystemResetBody(this)
            this.writer.writeCellLines({'/* Single In-the-Loop Component */' ...
                'if (xilFcnId == 0) {'});
            this.writer.incIndent;  
            
            % call SystemReset if applicable
            this.writeSectionSystemResetFunction;
                                                           
            this.writer.decIndent;
            this.writer.writeCellLines({'}' ...
                                        'else {'});
            this.writer.incIndent;
            this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
            this.writer.decIndent;
            this.writer.writeLine('}');                        
            this.writer.writeLine('return errorCode;');
        end        
        
        function ret = getUniqueRTWDataImplementationWithGroupedCSC(this, lRTWDataInterfaces)
            % Return a list of DataImplementation that represent
            % a set of unique grouped CSCs.
            lGroupedRTWDIs = this.getRTWDataInterfacesWithGroupedCSC(lRTWDataInterfaces);
            lGroupedCSCs = table( ...
                arrayfun(@(x)x.Implementation.ClassAccess.Package, lGroupedRTWDIs, 'UniformOutput', false)', ...
                arrayfun(@(x)x.Implementation.ClassAccess.ClassName, lGroupedRTWDIs, 'UniformOutput', false)');
            if ~isempty(lGroupedCSCs)
                [~, lUniqueIdx] = unique(lGroupedCSCs, 'stable');
                ret = [lGroupedRTWDIs(lUniqueIdx).Implementation];
            else
                ret = this.getCodeInfoUtils.getEmptyInterface('DataImplementation');
            end
        end

        function simFcnHeaderFiles = getStartupSimulinkVariantFcnHeaders(this)
            simFcnInfos = this.getCodeInfo.StartupSimulinkVariantFcnInfos;
            simFcnHeaderFiles = {};
            for aIdx = 1 : length(simFcnInfos)
                simVarFcnHeaderFile = simFcnInfos(aIdx).HeaderFile;
                if isempty(simVarFcnHeaderFile) 
                    continue;
                end
                simFcnHeaderFiles{end+1} = [simVarFcnHeaderFile '.h']; %#ok<AGROW> 
            end
        end
    
        function lHeaderFiles = getGroupedCSCHeaderFiles(this, lRTWDataInterfaces)
            % Return the list of (non-unique) header files for grouped
            % CSCs.
            lGroupedRTWDIs = this.getRTWDataInterfacesWithGroupedCSC(lRTWDataInterfaces);
            lHeaderFiles = arrayfun( ...
                @(x)x.Implementation.DeclarationFile, ...
                lGroupedRTWDIs, ...
                'UniformOutput', false);
    
            % Filter out any empty header file specifications
            lHeaderFiles(strcmp(lHeaderFiles, '')) = [];
            end
    
        function ret = getRTWDataInterfacesWithGroupedCSC(this, lRTWDataInterfaces)
            % Return the subset of data interfaces that are implemented with
            % a grouped CSCs
            ret = lRTWDataInterfaces( ...
                  arrayfun( ...
                  @(x)this.codeInfoUtils.isImportedGroupedCSC(x.Implementation), ...
                  lRTWDataInterfaces));
        end

        function writeCPPModelObjForIOAccess(this, storageImplementation)
            % write the Model Object access function if required for
            % IOAccess, this code is triggered when the ExternalIOAccess
            % parameter is set to Method or Structure-based method.
            
            if this.SILPILWrapperUtils.isCPPEncap
                switch this.codeInfoUtils.class(storageImplementation)
                    case 'ClassMethodExpression'
                        % for a Class Method Expression implementation always
                        % write out model object. The isDefined guard in the 
                        % caller means it won't be defined more than once. 
                        % i.e. storageImplementation is the rtObject.
                        modelObjectImplementation = storageImplementation.BaseRegion; %#ok<NASGU>
                        writeOutModelObj = true;
                        % Note: Strictly in this case we set the Owner twice, 
                        % to the same value, (in the caller and when we write 
                        % out the model object). This has been left as I 
                        % believe the code is more clear.
                    case 'StructAccessorVariable'
                        % for Struct Accessor Variable implementation only
                        % write out model object if it hasn't been defined
                        % already.
                        % Port data base structure (rtU/rtY) will be defined as
                        % normal below. i.e. storageImplementation is rtU/rtY
                        modelObjectImplementation = storageImplementation.Accessor.BaseRegion;
                        writeOutModelObj = ~modelObjectImplementation.isDefined;
                    otherwise
                        % no access method
                        writeOutModelObj = false;
                end

                if writeOutModelObj
                    % write out model object access function (rtObject) to
                    % allow method calls against it.
                    modelObjectImplementation = this.getCPPModelObjectImpl();

                    %Set owner on model object
                    this.setImplementationOwner(modelObjectImplementation, 'PIL');
                    dataExpression = this.getExpression(modelObjectImplementation);
                    this.writeModelObjectAccessFunction(dataExpression, modelObjectImplementation.Type.Identifier);
                end
            end
        end
        
        function ptrName = getModelObjectPtrAccessorName(~, modelObjName)
            ptrName = ['get_' modelObjName '_ptr'];
        end

        
        function expressionValue = writeGetSetAccessMethod(this, dataInterface, storageImplementation, isPointerToMatrix, storageVarIdentifier)
            % Create interface data for Get/Set Method access and return
            % the get/set call lines
            
            getArgModifier = '';
            if this.codeInfoUtils.isa(storageImplementation, 'StructAccessorVariable')
                % for struct accessor methods, get accessor
                % methods from parent application, and modify
                % get method argument call qualifier
                storageImplementation = storageImplementation.Accessor;
                if isa(dataInterface.Implementation,'coder.descriptor.PointerVariable')
                    getArgModifier = '';
                else
                    getArgModifier = '&';
                end
            end
            expressionValue = struct('Get','','Set','','IsParameterInterface',false);
            if ~isempty(storageImplementation.getReadExpression)
                if isPointerToMatrix
                    needsReplaceType = true;
                    typeIdentifier = this.getStorageTypeIdentifier( ...
                        storageImplementation, ...
                        needsReplaceType, ...
                        isPointerToMatrix);
                    typeCast = sprintf('( %s ) ', typeIdentifier);
                else
                    typeCast = '';
                end
                
                if rtw.connectivity.CodeInfoUtils.isStdArrayWithAccessMethod(storageImplementation)
                    snippet = sprintf('{\n');
                    % use the get method to obtain a pointer to the first
                    % element of the array
                    baseType = rtw.connectivity.CodeInfoTypeUtils.getBaseType( ...
                        storageImplementation.CodeType.ReferenceType);
                    snippet = sprintf('%sconst %s *xsd_temp = %s;\n',...
                        snippet, baseType.Identifier, storageImplementation.getReadExpression());
                    size = storageImplementation.CodeType.ReferenceType.Dimensions;
                    % assign the array values to the std::array xil 
                    % transfer variable
                    snippet = sprintf('%sfor (int32_T i = 0; i < %d; i++) {\n', snippet, size);
                    snippet = sprintf('%s%s[i] = xsd_temp[i];\n}\n}', snippet, storageVarIdentifier);
                    expressionValue.Get = snippet;
                else
                    expressionValue.Get = sprintf('%s = %s%s', storageVarIdentifier, typeCast, storageImplementation.getReadExpression);
                end
            end
            if ~isempty(storageImplementation.getWriteExpression)
                
                if rtw.connectivity.CodeInfoUtils.isStdArrayWithAccessMethod(storageImplementation)
                    % the set method expects a pointer, so get a pointer to
                    % the first element of the std::array xil transfer
                    % variable
                    storageVarIdentifier = sprintf('%s[0]', storageVarIdentifier);
                    getArgModifier = '&';
                end

                expressionValue.Set = sprintf('%s( %s%s )', ...
                    storageImplementation.getWriteExpression, ...
                    getArgModifier, storageVarIdentifier);
                if isempty(dataInterface.Timing)
                    % Need to spot Parameter set methods. Using
                    % a lack of timing information to do
                    % that. These won't be updated prior to
                    % calling Initialize, so we don't override
                    % any values in the generated code.
                    expressionValue.IsParameterInterface = true;
                end
            end
        end
        
        function definedData = writeInterfaceDataForPointerVariable( ...
                this, dataInterface, implementation, storageImplementation, ...
                dimsPreserved, isPointerToMatrix)
            % define a pointer variable statically initialized to the
            % storage implementation
            
            definedData = '';
            pointerVarImpl = rtw.connectivity.CodeInfoUtils.getNestedImplementation(implementation, 'PointerVariable');
            if ~isempty(pointerVarImpl)
                isPointerVarDefined = this.isDefined(pointerVarImpl);
                if isPointerVarDefined
                    % pointer variable is already defined, write extern
                    % declaration if needed
                    if ~rtw.connectivity.CodeInfoUtils.isDeclararedInHeader(pointerVarImpl)
                        isPointerType = true;
                        this.writer.writeLine(this.getDeclareExpression(pointerVarImpl, isPointerType));
                    end
                else
                    % Not aware of any cases that the pointer itself is
                    % volatile, so not going to support that concept
                    % (not sure if volatile would be flagged on the
                    % pointertype or on its basetype)
                    %
                    % storageVolatile will be used to correctly define
                    % the pointer as a pointer to volatile if
                    % required

                    % In-the-Loop owner
                    this.setImplementationOwner(pointerVarImpl, ...
                        'PIL');
                    definedData = this.getExpression(pointerVarImpl);

                    % define pointer
                    this.writer.writeLine('/* interface (pointer) data */');
                    isPointerVariable = true;
                    needToAllocatePtr = this.needToAllocate(dataInterface, isPointerVarDefined, isPointerVariable);
                    pointerDefineExpr = this.getPointerDefineExpression(pointerVarImpl, dataInterface, storageImplementation, dimsPreserved, needToAllocatePtr, isPointerToMatrix);
                    this.writer.writeLine('%s', pointerDefineExpr);
                end
                % add newline
                this.writer.newLine;
            end
        end
        
        function writeCustomFunctionCall(this, task)
            % Write a function call, in this case the expression returned
            % by the FunctionInterface getFunctionCall method is incorrect
            % so we need to reconstruct it
            
            assert(~this.SILPILWrapperUtils.isCPPEncap(), ...
                    'C++ classes are not supported by this implementation');
            callExpression = task.getFunctionCall();
            
            actualReturn = task.ActualReturn;
            actualArgs = task.ActualArgs;
            numActualArgs = length(actualArgs);
            allActualArgs = [actualReturn, actualArgs];
            allArgs = [task.Prototype.Return, task.Prototype.Arguments.toArray()];
         
            for argIdx = 1:length(allActualArgs)
                argImpl = allActualArgs(argIdx).Implementation;
                argType = allArgs(argIdx).Type;
                isArrayExpression = rtw.connectivity.CodeInfoUtils.isa(argImpl, 'ArrayExpression');
                if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(argImpl)
                    % the argument expression in the function call is
                    % tokenized

                    if isArrayExpression
                        % if a Selector block only takes a part of the
                        % array, the expressions already contains the
                        % offset index. remove the tokenized index.
                        callExpression = regexprep(callExpression, ...
                            regexptranslate('escape', '[$(INDEX_TOKEN_0)]'), '', 'once');
                    else
                        if argType.isPointer() || argType.isMatrix()
                            % replace expression with address, this is not
                            % handled by getFunctionCall
                            argExpression = argImpl.getExpression();
                            argAddress = sprintf('&(%s)', argExpression);
                            callExpression = regexprep(callExpression, ...
                                regexptranslate('escape', argExpression), argAddress, 'once');
                        end
                        % default to the first element of the array/matrix
                        elementIndex = '0';
                        callExpression = regexprep(callExpression, ...
                            regexptranslate('escape', '$(INDEX_TOKEN_0)'), elementIndex, 'once');
                    end
                elseif rtw.connectivity.CodeInfoUtils.isa(argImpl, 'AutosarExpression') || ...
                        (isArrayExpression && rtw.connectivity.CodeInfoUtils.isa(argImpl.BaseRegion, 'AutosarExpression')) || ...
                        (rtw.connectivity.CodeInfoUtils.isa(argImpl, 'StructExpression') &&  ...
                        rtw.connectivity.CodeInfoUtils.isa(this.getBaseStorageImplementation(argImpl), 'AutosarExpression'))
                    % the argument expression in the function call is
                    % missing or incomplete
                    [newArgumentExpr, argSearchExpr] = this.getArgumentExpressions(argImpl, argType);
                    callExpression = rtw.pil.TargetInterface.replaceArgumentInFunctionCall( ...
                        ~isempty(actualReturn), numActualArgs, argIdx, ...
                        argSearchExpr, newArgumentExpr, callExpression);
                elseif this.InterfaceStrategy.isInternalDataWithMappingToRTE(argImpl)
                    % the argument expression uses an incorrect identifier 
                    % and needs to be replaced with the correct RTE call
                    argExpression = argImpl.getExpression();
                    newArgumentExpr = this.getExpression(argImpl);
                    callExpression = regexprep(callExpression, argExpression, newArgumentExpr);
                end
            end
            this.writer.writeLine([callExpression ';']);
        end

        function [argumentExpr, searchExpr] = getArgumentExpressions(this, argImpl, argType)
            % get the correct argument expression and the expression to
            % search for in the incomplete function call

            if rtw.connectivity.CodeInfoUtils.isa(argImpl, 'ArrayExpression')
                indexExpression = this.getExpression(argImpl);
                % get the correct argument expression
                argumentExpr = [this.getExpression(argImpl.BaseRegion) indexExpression];
                if argType.isPointer()  || argType.isMatrix()
                    indexExpression = sprintf('&(%s)', indexExpression);
                    argumentExpr = sprintf('&(%s)', argumentExpr);
                end
                % for ArrayExpression we get the array index and we need to
                % escape it, e.g. [2] -> \[2\]
                searchExpr = regexptranslate('escape', indexExpression);
            elseif rtw.connectivity.CodeInfoUtils.isa(argImpl, 'StructExpression')
                argumentExpr = this.getArgumentExpressionForAutosarExpression( ...
                    argImpl, argType);
                % search for a partial expression e.g., the fields ".a.b"
                % from the complete argument expression 
                % "xsd_RPort_DE3_RPort_DE3.a.b"
                searchExpr = strrep(argumentExpr, ...
                    this.getExpression(this.getBaseStorageImplementation(argImpl)), '');
            else
                argumentExpr = this.getArgumentExpressionForAutosarExpression( ...
                    argImpl, argType);
                % nothing to search for, the argument is completely missing
                searchExpr = '';
            end
        end

        function argumentExpr = getArgumentExpressionForAutosarExpression(this, argImpl, argType)
            % use the getExpression implementation and access the address 
            % if needed
            argumentExpr = this.getExpression(argImpl);
            if argType.isPointer() && ~argImpl.Type.isPointer() || ...
                    argType.isMatrix() && ~argImpl.Type.isMatrix()
                argumentExpr = sprintf('&(%s)', argumentExpr);
            end
        end

        function fullExpression = getDefineExpressionFromImplementation(this, storageImplementation, xilDataInterface)
            % get an expression to define data for a DataInterface
            % Implementation
            storage_size_str = this.getStorageSizeStrAndIndex(...
                    storageImplementation, xilDataInterface.DimsPreserved);
                
                dataInterface = xilDataInterface.getRTWDataInterface;
                storageSpecifier = this.getStorageSpecOrMemSectionForXILDefinition(dataInterface);
                storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
                
                needsReplaceType = true;
                isPointerToMatrix = this.getIsPointerToMatrix(storageImplementation, dataInterface);
                typeIdentifier = this.getStorageTypeIdentifier( ...
                    storageImplementation, ...
                    needsReplaceType, ...
                    isPointerToMatrix);
                
                dataExpression = this.getExpression(storageImplementation);
                preMemSpecDirective = '';
                postMemSpecDirective = '';
                if matlab.internal.feature('XILDataAlignment')
                    memSpec = storageImplementation.getMemorySpecification();
                    if ~isempty(memSpec)
                        assert(isscalar(memSpec), 'Only one memory specification is expected per typed region');
                        switch memSpec.Position
                            case 'PRE_DIRECTIVE'
                                preMemSpecDirective = sprintf('%s ', memSpec.Specifier);
                            case 'POST_DIRECTIVE'
                                postMemSpecDirective = sprintf(' %s', memSpec.Specifier);
                            otherwise
                                assert(false, 'Unexpected memory specification position');
                        end
                    end
                end
                fullExpression = [preMemSpecDirective storageSpecifier storageVolatile typeIdentifier ' ' ...
                    dataExpression ...
                    storage_size_str postMemSpecDirective ';'];
        end

        function specifier = getStorageSpecOrMemSectionForXILDefinition(this, dataInterface)
            % Returns the storage specifier or any memory section
            if this.hasOnlyLocalAccess(dataInterface)
                % limit scope of internal variables
                specifier = 'static ';
            else
                if ~this.SILPILWrapperUtils.isCPPEncap
                    % only process C storage specifiers
                    tmpStorageSpec = this.codeInfoUtils.getStorageSpecifier(dataInterface.Implementation);
                    if ismember(tmpStorageSpec, {'unknown', 'extern', 'volatile'})
                        % ignore the following:
                        % - unknown, not a valid specifier
                        % - extern, this method is used to add specifiers
                        % to variable definitions in xil_interface. 
                        % For extern declarations see needExternDeclaration/getDeclareExpression
                        % - volatile, not a storage specifier
                        tmpStorageSpec = '';
                    end
                    specifier = sprintf('%s ', tmpStorageSpec);
                else
                    specifier = '';
                end
            end
        end

        function dataInterfaces = getStorageInterfacesFromInternalData(this, idxRange, useCachedDataInterfaces)
            % Get DataInterfaces from InternalData indices
            if useCachedDataInterfaces
                % Allow reusing DataInterfaces from cache (previously
                % created), this is our regular workflow
                dataInterfaces = rtw.pil.CodeInfoData.createDataInterfaces( ...
                    this.SILPILWrapperUtils, idxRange, 'internal');
            else
                % Don't use the cache and create new DataInterfaces, this
                % is needed when processing a parent codeDescriptor in
                % subsystem workflows
                allInternalData = this.getCodeInfoUtils().getCodeInfo().InternalData;
                internalDataForStorage = allInternalData(idxRange);
                useCache = false;
                filterInactiveInterfaces = false;
                dataInterfaces = rtw.pil.CodeInfoData.createDataInterfacesFromList(...
                    internalDataForStorage, idxRange, 'internal', ...
                    this.SILPILWrapperUtils, filterInactiveInterfaces, useCache);
            end
        end

        function [idxRange, rtmPointerImpl] = getInteralDataIdxForStorage( ...
                this, isImportedDialogParam)
            % SIL/PIL should not define data for dialog-box parameters that
            % are mapped to imported storage classes. These parameters are
            % not tunable and the user must provide the initial value at
            % definition. DataInterfaces representing the struct containing
            % these parameters must be filtered from InternalData to avoid
            % a double definition between xil_interface and the imported
            % code.
            rtmPointerImpl = [];
            componentInterface = this.getCodeInfoUtils().getCodeInfo();
            internalData = componentInterface.InternalData;
            importedDialogParameters = componentInterface.Parameters(isImportedDialogParam);
            
            numberToDiscard = numel(importedDialogParameters);
            importedNonTunableImplementations = coder.descriptor.Variable.empty(...
                0,numberToDiscard);
            
            for j=1:numberToDiscard
                importedNonTunableImplementations(j) = ...
                    this.SILPILWrapperUtils.getBaseStorageImplementation( ...
                    importedDialogParameters(j).Implementation );
            end
            
            dataInterfacesToKeep = true(1, length(internalData));
            
            if ~isempty(importedNonTunableImplementations)
                for i=1:length(internalData)
                    if any(internalData(i).Implementation == importedNonTunableImplementations)
                        % construct list of interfaces to filter out
                        dataInterfacesToKeep(i) = false;
                    end
                end
            end
            % return the indices of the InternalData that needs to be
            % defined
            idxRange = 1:length(internalData);
            idxRange = idxRange(dataInterfacesToKeep);
        end

        function taskCounterOffsetDataElements = getTaskCounterOffsetDataElements(this)
            taskCounterOffsetDataElements = rtw.pil.DataElement.empty;
            taskCounterOffsetData = this.codeInfoUtils.getTaskCounterOffsetData();
            if ~isempty(taskCounterOffsetData)
                for i = 1:length(taskCounterOffsetData)
                    taskCounterOffsetDataElements(end+1) = taskCounterOffsetData(i).getTargetDataElements(); %#ok<AGROW>
                end
            end
        end
    end
        
    methods (Access = public)
        
        function isDefined = isGetSetMemoryDefined(this, implementation)
            % Check if the memory used to cache the data accessed by 
            % the get/set method has already been defined. 
            % This function is different from "isDefined" as that refers
            % to the class object rather than the variable used by the
            % get/set method.
            % For data packed in a struct, e.g. parameters, the class
            % method accesses the whole struct and all data shares the same
            % method. The memory used by the method must be defined only
            % once.
            assert(this.codeInfoUtils.isa(implementation,'ClassMethodExpression'), ...
                'Unexpected class in isGetSetMemoryDefined');
            dataExpression = this.getExpression(implementation);
            % use the map of defined data to check for existing keys
            isDefined = this.isKeyInDefinedDataList(dataExpression);
        end
        
        function setImplementationOwner(this, implementation, owner) 
            this.InterfaceStrategy.setImplementationOwner(implementation, owner);
        end
        
        function owner = getImplementationOwner(this, implementation)
            owner = this.InterfaceStrategy.getImplementationOwner(implementation);
        end
        
        % getReadExpression needs to be public, because it gets called in
        % rtw.pil.CodeInfoData.writeOutputAssignment
        function readExpression = getReadExpression(this, implementation, cData, indices)
            % Returns the expression for reading from storage implementation
            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(implementation)
                readExpression = this.getExpressionForTLCCSC( ...
                    'ReadExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
            else
                readExpression = this.getElementExpression(implementation, cData, indices);
            end
        end
        
        function addressExpression = getAddressExpression(this, implementation, cData, indices)
            % Returns the expression for reading from storage implementation
            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(implementation)
                addressExpression = this.getExpressionForTLCCSC( ...
                    'AddressExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
            else
                addressExpression = sprintf('&(%s)', this.getElementExpression(implementation, cData, indices));    
            end
        end
        
        function writeExpression = getWriteExpression(this, implementation, cData, indices, writeString)
            % Returns the expression for writing to storage implementation
            if rtw.connectivity.CodeInfoUtils.canGetExpressionFromCodeInfo(implementation)
                writeExpression = this.getExpressionForTLCCSC( ...
                    'WriteExpression', ...
                    implementation, ...
                    cData, ...
                    indices);
                writeExpression = strrep(writeExpression, '$(NEWVALUE_TOKEN)', writeString);
            else
                expression = this.getElementExpression(implementation, cData, indices);
                writeExpression = [expression ' = ' writeString ';'];
            end
        end
        
        function fcnCallExpression = getFunctionCallExpressionForServerFunction(this, serverFunctionInfo)
            % getFunctionCall errors out for Simulink Functions, so we will
            % implement this ourselves.
            
            codeInfoPrototype = serverFunctionInfo.getCodeInfoData.Prototype;
            
            % When we're interfacing with the generated code, 
            % codeInfoPrototype is authoritative for both
            % ERT and AUTOSAR workflows.
            serverFunctionName = codeInfoPrototype.Name;
            
            if this.SILPILWrapperUtils.isCPPEncap
                modelObjectInterface = this.getCPPModelObjectImpl();
                modelObjectVariable = modelObjectInterface.getExpression;
                serverFunctionName = sprintf('%s.%s',modelObjectVariable,serverFunctionName);
            end
            
            % come up with the left-hand side expression as we could 
            % have a LHS return argument
            if serverFunctionInfo.hasReturnArg                
                returnArg = serverFunctionInfo.getDataInterfacesForArgs(...
                    serverFunctionInfo.getCodeArgPosition(codeInfoPrototype.Return));
                lhsExpression = this.getExpression(returnArg.Implementation);
                lhsExpression = [lhsExpression ' = '];
            else
                lhsExpression = '';
            end
            
            % add function name
            fcnCallExpression = [lhsExpression serverFunctionName '('];
            
            % add RHS arguments
            args = this.codeInfoUtils.getArray(codeInfoPrototype.Arguments);
            for codeInfoArgIdx = 1:length(args)
                arg = args(codeInfoArgIdx);
                codeArgPosition = serverFunctionInfo.getCodeArgPosition(arg);
                if serverFunctionInfo.isArgMultiInstanceRTM(codeArgPosition, arg)
                    % first argument to multi instance simulink function might be the self pointer
                    data = this.getCodeInfo.InternalData;
                    for i=1:length(data)
                        currData = data(i);
                        isDataRTMPointer = this.SILPILWrapperUtils.isRTMPointer(currData);
                        if isDataRTMPointer
                            argDataInterface = currData;
                            assert(this.codeInfoUtils.isa(argDataInterface.Implementation,...
                                'PointerVariable'),...
                                'RTModel should be a pointer variable');
                            break;
                        end
                    end
                else
                    isDataRTMPointer = false;
                    argDataInterface = serverFunctionInfo.getDataInterfacesForArgs(codeArgPosition);
                end
                argImp = argDataInterface.Implementation;
                isInputArg = strcmp(arg.IOType, 'INPUT');
                if isInputArg
                    storageImplementation = this.SILPILWrapperUtils.getBaseStorageImplementation(argImp);
                    storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
                    isScalar = ~rtw.connectivity.CodeInfoTypeUtils.isVector(...
                        storageType, this.SILPILWrapperUtils.getClientInterface);
                    if rtw.connectivity.CodeInfoTypeUtils.isBusOrStruct(argImp.Type) && isScalar && ~isDataRTMPointer
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    elseif isScalar && (argImp.Type.isPointer || arg.Type.isPointer) && ~isDataRTMPointer
                        % Scalar pass-by-pointer
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    else
                        fcnCallExpression = [fcnCallExpression getExpression(this, argImp) ',']; %#ok<AGROW>
                    end
                else % output or input-output
                    isVector = rtw.connectivity.CodeInfoTypeUtils.isVector(argImp.Type, ...
                        this.SILPILWrapperUtils.getSILPILInterface.getClientInterface);
                    if isVector
                        fcnCallExpression = [fcnCallExpression getExpression(this, argImp) ',']; %#ok<AGROW>
                    else
                        fcnCallExpression = [fcnCallExpression '&' getExpression(this, argImp) ',']; %#ok<AGROW>
                    end
                end
            end
            
            if strcmp(fcnCallExpression(end), ',')
                fcnCallExpression = fcnCallExpression(1:end-1);
            end
            fcnCallExpression = [fcnCallExpression ')'];
        end
    end

    methods (Access = protected)
        function isPointerToMatrix = getIsPointerToMatrix(this, storageImplementation, dataInterface)
            % Returns true if storageImplementation is a pointer to a
            % matrix.
            storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
            isPointerToMatrix = storageType.isPointer && ...
                ~isempty(dataInterface.Type) && ...
                dataInterface.Type.isMatrix && ...
                (this.SILPILWrapperUtils.getClientInterface.isArray(this.codeInfoUtils.getArray(dataInterface.Type.Dimensions)) || dataInterface.Type.ArrSizeOne);
        end
        
        function writeSectionEntryPointDeclarations(~)
            % no default implementation
        end
        
        function res = computeArgument(this, argument, storageImplementation, addArgumentName)
            
            arguments
                this (1,1) rtw.pil.TargetInterface
                argument (1,1) coder.descriptor.types.Argument
                storageImplementation (1,1) coder.descriptor.TypedRegion
                addArgumentName (1,1) logical = true;
            end
            
            % Could not find a prototype in model or subsystem code that
            % uses an ND array as input.
            dimsPreserved = false;
            storageSize = this.getStorageSizeStrAndIndex(...
                storageImplementation, dimsPreserved);

            res = this.computeArgumentType(argument.Type, storageImplementation);
            if addArgumentName
                res = sprintf('%s %s', res, argument.Name);
            end
            res = sprintf('%s%s', res, storageSize);
        end
        
        function res = computeArgumentType(this, type, storageImplementation)
            % Return a C/C++ identifier for the current type
            
            needsReplaceType = true;
            % pointer case is addressed below
            isPointerToMatrix = false;
            typeIdentifier = this.getStorageTypeIdentifier( ...
                storageImplementation, needsReplaceType, isPointerToMatrix);
            
            if type.isPointer()
                res = sprintf('%s *', typeIdentifier);
            elseif type.isReference()
                res = sprintf('&%s', typeIdentifier);
            else
                res = typeIdentifier;
            end
            
            storageVolatile = this.getStorageVolatileQualifier(storageImplementation);
            res = sprintf('%s%s', storageVolatile, res);
            if type.ReadOnly
                res = sprintf('const %s', res);
            end
        end
        
        function res = getFunctionDeclaration(this, functionInterface)
            % Return the function prototype for the current function
            % interface.
            % This method should be revised if needs to be used on C++ classes.
            
            prototype = functionInterface.Prototype;
            if isempty(prototype.Return)
                returnArg = 'void';
            else
                returnArg = this.computeArgument(...
                    prototype.Return, ...
                    functionInterface.ActualReturn.Implementation, ...
                    false);
            end
            numArgs = prototype.Arguments.Size();
            args = cell(1, numArgs);
            if numArgs < 1
                args = {'void'};
            else
                for idx = 1:prototype.Arguments.Size()
                    args{idx} = this.computeArgument(...
                        prototype.Arguments(idx), ...
                        functionInterface.ActualArgs(idx).Implementation);
                end
            end
            res = sprintf('%s %s(%s)', returnArg, prototype.Name,strjoin(args, ', '));
        end
        
        function dataInterfaces = getDataInterfacesForGetSetFunctionProcessingHook(~)
            dataInterfaces = [];
        end
        
        function dataInterfaces = getDataInterfacesForDynamicAccessFunctionsHook(~)
            dataInterfaces = [];
        end
        
        function needExtDecl = needExternDeclaration(this, implementation)
            
            needExtDecl = this.needExternDeclarationForCSC(implementation) || ...
                this.needExternDeclarationForVariable(implementation);
        end
               
        function needExtDecl = needExternDeclarationForVariable(~, ~)
            needExtDecl = false;
        end
    end
    
    methods (Access = protected, Static)
        function numElements = getStorageNumElements(storageImplementation)
            % Return the number of storage elements
            if isempty(storageImplementation)
                numElements = 0;
                return;
            end
            storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
            numElements = rtw.connectivity.CodeInfoTypeUtils.getSymbolicWidth(storageType);
        end
        
        function isConstructorArg = getIsInterfaceConstructorArg(xilInterface)
            isConstructorArg = strcmp(xilInterface.getDataInterfaceType, 'constructorArg');
        end

        function storageVolatile = getStorageVolatileQualifier(storageImplementation)
            % Returns the storage volatile qualifier when necessary
            storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
            isVolatile = ~isempty(storageImplementation) && ...
                rtw.connectivity.CodeInfoTypeUtils.isVolatile(storageType);
            if isVolatile
                % add volatile qualifier
                storageVolatile = 'volatile ';
            else
                storageVolatile = '';
            end
        end
        
        function retExpression = getExpressionForTLCCSC(expressionType, implementation, cData, lOriginalLeafIndices)
            % Get expression for accessing data element for CSC of type
            % "Other".
            [lOriginalElementSubExpression, nOriginalLevels] = cData.getElementExpression(lOriginalLeafIndices, true, true);
            if cData.getDataInterface.DimsPreserved
                lLevelIndexExprPreFormatting = 'i%d_0';
            else
                lLevelIndexExprPreFormatting = 'i%d';
            end
            lSymbolicZeroLevelIndexExpr = sprintf(lLevelIndexExprPreFormatting,0);
            
            isArrayExpression =  rtw.connectivity.CodeInfoUtils.isa(implementation, 'ArrayExpression');
            if isArrayExpression
                % Handle the case where the CustomVariable is an element of an
                % ArrayExpression.
                %
                % For example, if the underlying variable is:
                %   real_T exampleSignal[10];
                %
                % But the current port is a 5-element array:
                %   exampleSignal[5] -- exampleSignal[9]
                %
                lOffset = implementation.Offset;
                implementation = implementation.BaseRegion;
                
                % Determine whether we need to prepend a leaf index, or a
                % non-leaf index.
                %
                [needToPrependLeafIndex, needToPrependNonLeafIndex] = ...
                    rtw.pil.TargetInterface.needToPrependForArrayExpression(lOriginalElementSubExpression);
                
                % If we prepended a non-leaf index, then update the number
                % of levels
                if needToPrependNonLeafIndex
                    nLevels = nOriginalLevels + 1;
                    % Hard-code the index for the offset, since there won't
                    % be a loop generated for it.
                    lZeroLevelIndexExpr = sprintf('%d', lOffset);
                else
                    nLevels = nOriginalLevels;
                    lZeroLevelIndexExpr = lSymbolicZeroLevelIndexExpr;
                end
                
                lElementSubExpression = ...
                    rtw.pil.TargetInterface.updateElementSubExpressionForArrayExpression( ...
                        lOriginalElementSubExpression, ...
                        lOriginalLeafIndices, ...
                        lZeroLevelIndexExpr, ...
                        needToPrependLeafIndex, ...
                        needToPrependNonLeafIndex);
                
                % Update the indices expression
                lUpdatedLeafIndices = ...
                    rtw.pil.TargetInterface.updateLeafIndicesForArrayExpression( ...
                    lOriginalLeafIndices, ...
                    nLevels, ...
                    lOffset);
                
                % Update the zero level index expression
                if ~needToPrependNonLeafIndex && ~needToPrependLeafIndex && lOffset ~= 0
                    % If an array expression, and we didn't prepend anything
                    % we need to add the offset to the lowest level non-leaf
                    % index if the offset is non-zero
                    lUpdatedZeroLevelIndexExpr = sprintf('(%s + %d)', lZeroLevelIndexExpr, lOffset);
                else
                    lUpdatedZeroLevelIndexExpr = lZeroLevelIndexExpr;
                end
            else
                needToPrependNonLeafIndex = false;
                nLevels = nOriginalLevels;
                lZeroLevelIndexExpr = lSymbolicZeroLevelIndexExpr;
                lUpdatedZeroLevelIndexExpr = lZeroLevelIndexExpr;
                lUpdatedLeafIndices = lOriginalLeafIndices;
                lElementSubExpression = lOriginalElementSubExpression;
            end
            
            % Get the "candidate" element identifiers from the
            % CustomVariable object - need to use the "original" indices
            % here, since that is what will have been used in the original
            % lElementSubExpression
            dataElements = rtw.connectivity.CodeInfoUtils.getArray(implementation.DataElements);
            lCandidateElementIdentifiers = arrayfun(@(x)(x.DataElementIdentifier), dataElements, 'UniformOutput', false);
            lCandidateElementIdentifiers = cellfun(@(x)rtw.pil.TargetInterface.substituteTokens(x, nLevels, lOriginalLeafIndices, lZeroLevelIndexExpr, lLevelIndexExprPreFormatting, needToPrependNonLeafIndex), ...
                lCandidateElementIdentifiers, ...
                'UniformOutput', false);
            
            % Find the correct identifier - the one that matches
            % lElementSubExpression.
            lCurrentElement = dataElements(strcmp(lCandidateElementIdentifiers, lElementSubExpression));
            assert(~isempty(lCurrentElement), 'Could not find codeinfo data for %s', lElementSubExpression);
            retExpression = lCurrentElement.(expressionType);
            
            % Substitute index tokens into the final expression
            retExpression = rtw.pil.TargetInterface.substituteTokens( retExpression, nLevels, lUpdatedLeafIndices, lUpdatedZeroLevelIndexExpr, lLevelIndexExprPreFormatting, needToPrependNonLeafIndex);
        end
        
        function [needToPrependLeafIndex, needToPrependNonLeafIndex] = needToPrependForArrayExpression(lOriginalElementSubExpression)
            % Determine whether we need to prepend an index into an array
            % for an ArrayExpression.
            %
            % Prepend a "leaf" index if the port corresponds to
            % a real-valued scalar element of the underlying vector.
            % 
            %   e.g., for an underlying variable
            %       real_T myVariable[3];
            %
            %   The port corresponds to myVariable[1]
            needToPrependLeafIndex = isempty(lOriginalElementSubExpression);
            
            % Prepend a "non-leaf" index if the port corresponds to an
            % element of a composite data type
            %
            % For example, for the underlying variable
            %       myBus myBusInstance[3];
            %
            % The port corresponds to myBusInstance[1].myField[xyz]
            %
            needToPrependNonLeafIndex = ~needToPrependLeafIndex && ~startsWith(lOriginalElementSubExpression, '[');
        end
            
        function updatedLeafIndices = updateLeafIndicesForArrayExpression(leafIndices, nLevels, lOffset)
            % When working with an array expression, update the leaf
            % indices based on the number of levels in the DataElement
            % hierarchy and the offset in the array expression
            updatedLeafIndices = leafIndices;
            if nLevels == 0
                if lOffset ~= 0
                    if isnumeric(updatedLeafIndices)
                        % If indices is numeric, then add the offset
                        % so that it can be hard-coded in the returned
                        % expression.
                        %
                        % Example:
                        %
                        % Suppose that our ports correspond to elements
                        % 5-9 of the underlying variable:
                        %   real_T myVariable[10];
                        % Then to access element 2 of the port
                        %   leafIndices = 2, 
                        %   lOffset = 5,
                        %  and we compute:
                        %   updatedLeafIndices = 7;
                        updatedLeafIndices(1) = updatedLeafIndices(1) + lOffset;
                    else
                        % Example:
                        %
                        % for a port that corresponds of elements 5-9 of an
                        % underlying variable:
                        %   real_T myVariable[10]
                        % To access element "myElem" of the port, return 
                        % "(myElem + 5)"
                        %
                        assert(ischar(updatedLeafIndices));
                        updatedLeafIndices = sprintf('(%s + %d)', updatedLeafIndices, lOffset);
                    end
                end
            end
        end
        
        function lUpdatedElementSubExpression = updateElementSubExpressionForArrayExpression(lElementSubExpression, lOriginalLeafIndices, lZeroLevelIndexExpr, needToPrependLeafIndex, needToPrependNonLeafIndex)
            % If the ArrayExpression only indexes one element of
            % the array, then even though the underlying data is an
            % index into an array, the "Type" associated with the
            % ArrayExpression is scalar.  So we need to patch the
            % element sub expression with an index into the first
            % element of the array.
            %
            % We might be indexing into the "leaf" data element, or the
            % lowest "level" of a hierarchy of non-leaf data elements.
            if needToPrependLeafIndex
                if isnumeric(lOriginalLeafIndices)
                    lUpdatedElementSubExpression = sprintf('[%d]%s', lOriginalLeafIndices, lElementSubExpression);
                else
                    assert(ischar(lOriginalLeafIndices));
                    lUpdatedElementSubExpression = sprintf('[%s]%s', lOriginalLeafIndices, lElementSubExpression);
                end
            elseif needToPrependNonLeafIndex
                % We need to update the sub expression by prepending
                % with an explicit index into the base variable.
                lUpdatedElementSubExpression = sprintf('[%s]%s', lZeroLevelIndexExpr, lElementSubExpression);
            else
                % Return the original expression.
                lUpdatedElementSubExpression = lElementSubExpression;
            end
        end
        
        function lExpression = substituteTokens( lExpression, nLevels, indices, lZeroLevelIndexExpr, lLevelIndexExprPreFormatting, indexWasPrepended)
            % Fill in the tokens for ancestor indices
            for kLevel = 0:nLevels-1
                if kLevel == 0
                    lIToken = lZeroLevelIndexExpr;
                elseif indexWasPrepended
                    % If we prepended a hard-coded, this won't be included in
                    % the "for" loop that is generated in xil_interface.c.
                    % So we need to subtract 1 from the identifier in 
                    % expressions "i1", "i2", "i3", etc.  i.e.,
                    % "$(INDEX_TOKEN_1)" will map to "i0".
                    lIToken = sprintf(lLevelIndexExprPreFormatting, kLevel-1);
                else
                    lIToken = sprintf(lLevelIndexExprPreFormatting, kLevel);
                end 
                lExpression = strrep( ...
                    lExpression, ...
                    sprintf('$(INDEX_TOKEN_%d)', kLevel), ...
                    lIToken);
            end
            
            % Fill in the tokens for the leaf index
            if iscell(indices)
                assert(isscalar(indices), 'PreserveDimensions not supported for CSCs of type Other');
                indices = indices{1};
            end

            if isnumeric(indices)
                indices = sprintf('%d', indices);
            end 
            lExpression = strrep( ...
                lExpression, ...
                sprintf('$(INDEX_TOKEN_%d)', nLevels), ...
                indices);
        end
    end
    
    methods (Access = {?rtw.pil.TargetInterface, ?coder.internal.connectivity.TargetInterfaceStrategy})
        % Give access to subclasses and Strategy hierarchy
        
        % generate default initialization for a data interface 
        % (e.g. outport) implementation owned by the In-the-Loop application
        function writeDataInterfaceDefaultInitialization(this, dataElements)                        

            for dataElementIdx = 1:length(dataElements)                
                cData = dataElements(dataElementIdx);
                
                implementation = cData.getDataInterface.getRTWDataInterface.Implementation;
                if this.codeInfoUtils.isa(implementation, 'ClassMethodExpression') || ...
                            this.codeInfoUtils.isa(implementation, 'BasicAccessFunctionExpressionCollection') ||...
                            (~this.codeInfoUtils.isa(implementation, 'TypedCollection') && ...
                            rtw.connectivity.CodeInfoTypeUtils.isaType(implementation.Type, 'Class') && ...
                            (strcmp(implementation.Type.Identifier,'std::string') || ...
                             contains(implementation.Type.Identifier,'std::array') || ...
                             contains(implementation.Type.Identifier,'std::vector'))) || ...
                            (cData.getDataInterface.NeedsIOTargetTransform && ...
                            (strcmp(cData.SimulinkType,'char'))) || ...
                            cData.getDataInterface.HasTargetSideOpaqueRegion || ...
                            (strcmp(cData.SimulinkType,'half'))
                    % If this is a class method, then it's already been
                    % initialized above.                    
                    continue;
                end
                
                baseStorageImplementationList = this.getBaseStorageImplementation(implementation);
                %If more than on StorageImplementation returned, need to
                %identify which one matches this element.
                if ~isscalar(baseStorageImplementationList)
                    elementIndex = cData.IndexInArrayImplementation;
                    implementation = baseStorageImplementationList(elementIndex);
                else
                    elementIndex = 1;
                end
                
                baseStorageImplementation = baseStorageImplementationList(elementIndex);                
                owner = this.getImplementationOwner(baseStorageImplementation);             
                if strcmp(owner, 'PIL')
                    % open any required loops
                    cData.writeStructArrayHeader(this.writer);                    
                    initString = cData.getDefaultInitialization(this.SILPILWrapperUtils);
                    isArray = rtw.pil.TargetInterface.isArrayInCode(...
                        implementation.Type, cData.getNumElements);
                    if isArray
                        this.writer.wBlockStart('');
                        
                        % enum ImportFromFile parameter
                        elementIndexVariables = ...
                            cData.writeElementLoopHeader(this.writer);
                        
                        % get an expression to use in the loop
                        initExpression = this.getInitializeExpression(implementation, cData, elementIndexVariables, initString);
                        this.writer.writeLine('%s', initExpression);
                        
                        cData.writeElementLoopTrailer(this.writer);                        
                                                
                        this.writer.wBlockEnd;
                    else
                        initExpression = this.getInitializeExpression(implementation, cData, 0, initString);
                        this.writer.writeLine('%s', initExpression);
                    end
                    % close any required loops
                    cData.writeStructArrayTrailer(this.writer);
                end                                                
            end                                                                     
        end
        
        function argInfo = getCallerArgInfo(this, callerFunctionInfo, codeInfoArg, codeArgPosition)
            
            % initialize structure
            argInfo = struct(...
                'ArgName',[],...
                'IsInArg',[],...
                'IsOutArg',[],...
                'IsInOutArg',[],...
                'IsVector',[],...
                'IsVoidPointer',[],...
                'NumElements', [], ...
                'IsBus',[],...                
                'StorageSizeStr',[],...
                'ArgType',[],...
                'ArgBaseType',[],...
                'MemcpyExpression',[],...
                'Implementation',[]...
                );
            
            [argDataInterface, argCData] = this.InterfaceStrategy.getCallerArgumentDataInterfaceAndElement( ...
                callerFunctionInfo, codeInfoArg, codeArgPosition);

            % Determine the argument name and whether it is input or output
            % argument            
            argInfo.ArgName = callerFunctionInfo.getCodeInfoArgumentName(codeInfoArg);
            argInfo.IsInArg = strcmp(codeInfoArg.IOType, 'INPUT');
            argInfo.IsOutArg = strcmp(codeInfoArg.IOType, 'OUTPUT');
            argInfo.IsInOutArg = strcmp(codeInfoArg.IOType, 'INPUT_OUTPUT');
            argInfo.IsVoidPointer = callerFunctionInfo.isVoidPointerTypedArg(codeArgPosition);
            
            % determine whether we need the const qualifier for input args
            argInfo.Implementation = argDataInterface.Implementation;
            storageImplementation =...
                this.SILPILWrapperUtils.getBaseStorageImplementation(...
                argInfo.Implementation);
            storageType = rtw.connectivity.CodeInfoTypeUtils.getStorageTypeFromImplementation(storageImplementation);
            argInfo.IsVector = rtw.connectivity.CodeInfoTypeUtils.isVector(...
                storageType, ...
                this.SILPILWrapperUtils.getClientInterface);            
            argInfo.NumElements = rtw.connectivity.CodeInfoTypeUtils.getSymbolicWidth(storageType);
            argInfo.IsBus = rtw.connectivity.CodeInfoTypeUtils.isBusOrStruct(...
                argInfo.Implementation.Type);
            
            % determine the storage size string for multidimensional
            % signals
            argInfo.StorageSizeStr = this.getCallerArgStorageSizeString(argInfo);
            
            % determine the argument type and memcpy expressions            
            if argInfo.IsInOutArg
                % code treats this as an output arg
                [argInfo.ArgType,argInfo.ArgBaseType] =...
                    this.getCallerOutputArgType(argInfo, argDataInterface, argCData);
                % copy from input to xil storage
                argInfo.MemcpyExpression{1} =...
                    this.getCallerInputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                % copy from xil storage to output
                argInfo.MemcpyExpression{2} =...
                    this.getCallerOutputArgMemcpyExpression(argInfo, argDataInterface, argCData);
            else
                if argInfo.IsInArg
                    [argInfo.ArgType,argInfo.ArgBaseType] =...
                        this.getCallerInputArgType(argInfo, argDataInterface, argCData);
                    argInfo.MemcpyExpression =...
                        this.getCallerInputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                else
                    [argInfo.ArgType,argInfo.ArgBaseType] =...
                        this.getCallerOutputArgType(argInfo, argDataInterface, argCData);
                    argInfo.MemcpyExpression =...
                        this.getCallerOutputArgMemcpyExpression(argInfo, argDataInterface, argCData);
                end
            end
        end
    end
    
    methods (Static, Access = {?rtw.pil.TargetInterface, ?coder.internal.connectivity.TargetInterfaceStrategy})

       function isGetSetFunctionAccess = isGetSetFunctionAccess(implementation)
            % determines whether we use get / set functions to access the
            % dataInterface in the generated code     

            isGetSetFunctionAccess = ...
                rtw.pil.TargetInterface.isGetSetCustomExpression(implementation) && ...
                ~rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation);
       end
    end

    methods (Access = {?rtw.pil.TargetInterface, ?coder.internal.connectivity.TargetInterfaceStrategy})

        function hasOnlyLocalAccess = hasOnlyLocalAccess(this, dataInterface)
            % override in subclass     

            % determine the data interfaces for all the server and caller
            % functions arguments
            serverFunctionsInfo = this.SILPILWrapperUtils.getServerFunctionsInfo();
            callerFunctionsInfo = this.getCallerFunctionsInfo();
            serverAndCallerFunctionsDataInterfaces = [];
            for svrIdx = 1:length(serverFunctionsInfo)
                serverAndCallerFunctionsDataInterfaces =...
                    [serverAndCallerFunctionsDataInterfaces...
                    serverFunctionsInfo(svrIdx).getAllDataInterfaces]; %#ok<AGROW>
            end
            
            for callerIdx = 1:length(callerFunctionsInfo)
                serverAndCallerFunctionsDataInterfaces =...
                    [serverAndCallerFunctionsDataInterfaces...
                    callerFunctionsInfo(callerIdx).getAllDataInterfaces]; %#ok<AGROW>
            end
            
            foundInterface = false;
            for idx = 1:numel(serverAndCallerFunctionsDataInterfaces)
                if isequal(serverAndCallerFunctionsDataInterfaces(idx), dataInterface)
                    foundInterface = true;
                    break;
                end
            end
            if rtw.pil.TargetInterface.isGetSetFunctionAccess(dataInterface.Implementation) || ...
                    foundInterface || ...
                    isa(dataInterface.Implementation, 'coder.descriptor.ClassMethodAccess')
                % 1) intermediate get/set function storage is static
                % 2) Caller and server functions arguments are static
                hasOnlyLocalAccess = true;
            else
                hasOnlyLocalAccess = false;
            end
        end
    end 

    methods (Static, Access = private)
        function callExpression = replaceArgumentInFunctionCall( ...
                hasActualReturn, numActualArgs, argIdx, argSearchExpr, newArgument, callExpression)
            % replace an argument in the incomplete function call

            if hasActualReturn && argIdx == 1
                % return argument, ? = func()
                searchExpression = sprintf('%s\\s*=', argSearchExpr);
                callExpression = regexprep(callExpression, searchExpression, sprintf('(%s) =', newArgument), 'once');
            elseif (~hasActualReturn && argIdx == 1) || (hasActualReturn && argIdx-1 == 1)
                % first argument
                if numActualArgs == 1
                    % no other args in the function, func(?)
                    searchExpression = sprintf('\\(%s\\)', argSearchExpr);
                    callExpression = regexprep(callExpression, searchExpression, sprintf('(%s)', newArgument), 'once');
                else
                    % at least one arg after, func(?, arg2)
                    searchExpression = sprintf('\\(%s,', argSearchExpr);
                    callExpression = regexprep(callExpression, searchExpression, sprintf('(%s,', newArgument), 'once');
                end
            elseif (~hasActualReturn && argIdx < numActualArgs) || (hasActualReturn && argIdx-1 < numActualArgs)
                % middle argument, func(arg1, ?, ?, argN)
                searchExpression = sprintf(', %s,', argSearchExpr);
                callExpression = regexprep(callExpression, searchExpression, sprintf(', %s,', newArgument), 'once');
            elseif (~hasActualReturn && argIdx == numActualArgs) || (hasActualReturn && argIdx-1 == numActualArgs)
                % last argument func(arg1, arg2, ?)
                searchExpression = sprintf('%s\\)$', argSearchExpr);
                callExpression = regexprep(callExpression, searchExpression, sprintf('%s)', newArgument), 'once');
            end
        end

        function isGetSetCustomExpression = isGetSetCustomExpression(implementation)
            % return true if the implementation is a get/set
            % CustomExpression
            %
            % assume that any time the Read / Write expressions don't
            % match, we have a GetSet CSC
            %
            % CodeInfo has no way to differentiate between the GetSet CSC
            % and a user's custom CSC that could potentially also register
            % different Read / Write expressions
            isGetSetCustomExpression = ...
                rtw.connectivity.CodeInfoUtils.isa(implementation, 'CustomExpression') && ...
                ~strcmp(implementation.ReadExpression, implementation.WriteExpression);
        end

        function isGetSetWithOR = isGetSetFunctionAccessWithOpaqueRegion(implementation)
            % return true if the implementation is a get/set
            % CustomExpression that should be processed via OpaqueRegion
            isGetSetWithOR = rtw.pil.TargetInterface.isGetSetCustomExpression(implementation) && ...
                rtw.connectivity.CodeInfoUtils.shouldUseOpaqueRegion(implementation);
        end
    end
end
