
classdef (Hidden = true) SILPILWrapperUtils < coder.connectivity.WrapperUtils
%SILPILWRAPPERUTILS SIL/PIL code interface utilities
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2010-2020 The MathWorks, Inc.

    % properties
    properties (SetAccess = 'private', GetAccess = 'protected')
        SILPILInterface;
        SigLogInfo;
        CommandData;
        MemUnitInfo;
        TimerType;
        ProfilingTaskRegistry;            
        ProfilingOverheadRegistry = [];
        ProfilingCaptureRegistry = [];
        TypeUtils;
        ComponentArgs;
    end
    
    properties (SetAccess = 'protected', GetAccess = 'public')
        SILDebuggingInfo;
        CodeInstrumentationEnabled;
        ProfilingEnabled;
        CodeProfilingSaveOptions;
        CodeExecutionProfileVariable;
        % this variable contains the unit of measurement to use when it does
        % not depend on TicksPerSecond: i.e., with HW counters
        ProfilingHWCounterUnit;
        InportDirectFeedThrough;
        IsUpdateModelReferenceTargets;
        ComponentBoundaryValidation;
        MF0Model;
        MF0ModelInfo;
        SupportedTunableModelWorkspaceParamsString = '';
    end
    
    properties(SetAccess = 'public', GetAccess = 'private')
        ParamIODatatypes; % cache of all parameter I/O datatypes        
        SupportedTunableWorkspaceParams; % indices of supported tunable workspace params
        SupportedTunableBaseWorkspaceParams;
        SupportedTunableModelWorkspaceParams;
        UnsupportedTunableParameterWarningInfo; % details of unsupported tunable params
        LoggingIODataTypes; % cache of all signal logging I/O datatypes
        AllIODataTypes;
    end

    properties (SetAccess = 'private', GetAccess = 'protected')
        % this map contains the original values of those top-model parameters
        % which have been over-written during the build process
        TopModelOriginalParams;
    end
    
    properties (Access = 'private')
        ClientInterface;        
        IsGRTBased = false;
        ValidateCodeInfoUtils = true;
    end
    
    properties (Access = 'public')
        % flag to indicate that boolean data type remapping has not yet
        % been validated
        NeedToValidateBooleanRemapping = true;
    end
    
    properties (Access = 'public', NonCopyable = true)
        % do not copy this property on copy operations
        CodeInfoDataCache; % cache of CodeInfoData objects
        %
        AreEventCallbacksRequired = false;
    end
    
    properties (SetAccess = 'private', GetAccess = 'protected', NonCopyable = true)
        % do not copy this property on copy operations
        SignalLoggingData;
        SigLogData;
        StateLoggingData;
    end
        
    methods
        % constructor
        function this = SILPILWrapperUtils(silPILInterface, ...
                                    infoStruct, ...
                                    clientInterface, ...
                                    lTopModelOriginalParams, ...
                                    lXilCompInfo)
            
            

            % validate arg
            %rtw.connectivity.Utils.validateArg(codeInfoUtils, ...
            %    'rtw.connectivity.CodeInfoUtils'); 
            rtw.connectivity.Utils.validateArg(silPILInterface, ...
                'rtw.pil.SILPILInterface');
            
            rtw.connectivity.Utils.validateArg(clientInterface, ...
                'coder.connectivity.ClientInterface');
                        
            configInterface = clientInterface.createConfigInterfaceFromInfoStruct(infoStruct, silPILInterface.getCodeGenModel);
                                                
            % call super class constructor                             
            this@coder.connectivity.WrapperUtils(infoStruct, configInterface);

            this.TopModelOriginalParams = lTopModelOriginalParams;
            this.SILPILInterface = silPILInterface;            
            this.ClientInterface = clientInterface;
            this.IsUpdateModelReferenceTargets = this.SILPILInterface.IsUpdateModelReferenceTargets;
            
            lBoundaryValidationNeedsHardError = ~this.SILPILInterface.IsUpdateModelReferenceTargets;
            this.ComponentBoundaryValidation = rtw.pil.ComponentBoundaryValidation(lBoundaryValidationNeedsHardError);
            
            
            if this.getSILPILInterface.getInTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn
                % Load serialized MF0 from XML
                [this.MF0ModelInfo, lParser] = this.getSILPILInterface.getMF0ModelInfo();
                this.MF0Model = lParser.Model;  % Need to persist model
                
                % Cache in configInterface
                configInterface.setMF0ModelInfo(this.MF0ModelInfo, this.MF0Model);
                
                % Check if top model XIL
                configInterface.setIsTopModelXILSim(this.getSILPILInterface.isTopModelXILSim);
            end
            
            if this.getSILPILInterface.getInTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn
                activeConfigSet = configInterface.getResolvedConfig;
                this.IsGRTBased = activeConfigSet.getComponent('Code Generation').getComponent('Target').isGRTTarget;                
            end
            className = '';
            if this.getSILPILInterface.getInTheLoopType == rtw.pil.InTheLoopType.MATLABFcn
                className =  this.getParam('CppInterfaceClassName');
            end
            % validate the configuration interface before initializing
            % CodeInfoUtils
            this.validateConfigInterface;
            
            % initialize CodeInfoUtils / CoderInfo
            this.resetCodeInfoUtils;                                                                     
                     
            % store setting of SILDebugging
            this.SILDebuggingInfo = rtw.connectivity.SILDebuggingInfo(...
                strcmp(this.getInstrumentationSettingsModelParam('SILDebugging'), 'on'), ...
                this.isCPPEncap, ...
                this.getCodeInfo, ...
                className);
            
            % execution profiling
            lCodeExecutionProfilingEnabled = initExecutionProfilingSettings(this);
            
            % create the componentArgs object
            this.ComponentArgs = this.createComponentArgs...
                (lCodeExecutionProfilingEnabled, lXilCompInfo);
            
            targetServices = this.getTargetServices();
                        
            try
                targetServices.initialize(this.ComponentArgs);
            catch eConfig
                % construct standard error with eConfig cause
                [pilId, pilMessage] = rtw.pil.ProductInfo.message('pil', ...
                                                                 'ConfigClassError', ...
                                                                  targetServices.getName());
                pilException = MException(pilId, ...
                    pilMessage);
                pilException = pilException.addCause(eConfig);                
                throw(pilException);
            end
            
            % generic validation
            this.validateCodeInterface;
            % initialize memUnitInfo
            this.initMemUnitInfo;   
            % initialize rtiostream hooks
            this.initRtIOStreamHooks;
            
            % code instrumentation
            this.CodeInstrumentationEnabled = false;
            
            % initialize CommandData
            this.initCommandData;
                
            % initialize empty LoggingDataInfo
            this.SignalLoggingData = [];
            this.StateLoggingData = [];
                        
            this.validateParameters;
            
            % set up TypeUtils instance for relevant tool.            
            switch (this.getSILPILInterface.getInTheLoopType)
                case rtw.pil.InTheLoopType.MATLABFcn

                    this.TypeUtils = rtw.connectivity.MATLABTypeUtils;
                    
                case {  rtw.pil.InTheLoopType.ModelBlock, ...
                        rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                        rtw.pil.InTheLoopType.Block, ...
                        rtw.pil.InTheLoopType.Subsystem}
            
                    this.TypeUtils = rtw.connectivity.SimulinkTypeUtils;

                otherwise
                    assert(false,'Invalid InTheLoopType');
            end
        end                
        
        function isCoderAssumptionsEnabled = isCoderAssumptionsEnabled(this)
            % ComponentArgs hold the truth about whether the feature is
            % enabled
            isCoderAssumptionsEnabled = ...
                this.getComponentArgs().CoderAssumptionsEnabled;
        end
        
        function DFindices = getDFInputPortsIndices( this, inports )
            if (~this.isOutputUpdateRequired)
                % If it is an export function, return all the inports. Indeed
                % for export functions all the input ports are passed
                % through the output function
                DFindices = inports;
            else
                DFindices = getInputPortIndices(this, inports, true);
            end
        end
        
        function NonDFindices = getNonDFInputPortsIndices( this, inports )
            if (~this.isOutputUpdateRequired)
                % If it is an export function, return empty. Since
                % for export functions all the input ports are passed
                % through the output function, there is no need for
                % checking NonDF input ports.
                NonDFindices = [];
            else
                NonDFindices = getInputPortIndices(this, inports, false);
            end
        end       
        
        
        % This function returns true if the model has at least one input port
        % that does not have direct feedthrough.   I.e. these inports must
        % be accessed in mdlUpdate rather than mdlOutputs.
        function y = hasInputPortsWithoutDirectFeedthrough(this)
            % Check that the model has inports
            if isempty(this.InportDirectFeedThrough)
                y = false;
            else
                % Filter out Inports with DirectFeedthrough
                % Inports can be a single matrix or a cellarray,
                % depending by its cardinality
                y = any(~this.InportDirectFeedThrough);
            end
        end
        
        
        function config = getTypeUtils(this)
            % return the Type Utils cached
            config = this.TypeUtils;
        end
        
        function value = getTargetServices(this)
            value = this.getSILPILInterface().getTargetServices();
        end
        
        function value = getComponentArgs(this)
            value = this.ComponentArgs;
        end

        function clientInterface = getClientInterface(this)
            clientInterface = this.ClientInterface;
        end
        
        function setProfilingTaskRegistry(this, registry)            
            this.ProfilingTaskRegistry = registry;            
        end
        
        function registry = getProfilingTaskRegistry(this)            
            registry = this.ProfilingTaskRegistry;            
        end
        
        function setProfilingCaptureRegistry(this, registry)            
            this.ProfilingCaptureRegistry = registry;            
        end
        
        function registry = getProfilingCaptureRegistry(this)
            registry = this.ProfilingCaptureRegistry;
        end
        
        function setProfilingOverheadRegistry(this, registry)            
            this.ProfilingOverheadRegistry = registry;            
        end
        
        function registry = getProfilingOverheadRegistry(this)
            registry = this.ProfilingOverheadRegistry;
        end
        
        function [lComponentName, lComponentRef] = getComponentName(this)
            lComponentPath = this.SILPILInterface.getFullComponentPath;
            [lComponentName, lComponentRef] = ...
                this.getClientInterface.getComponentName(lComponentPath);
        end
        
        function initCodeInstrumentation(this)
        % is code instrumentation enabled
            codeDir = this.SILPILInterface.getCodeDir;
            
            infoStruct = this.SILPILInterface.getInfoStruct;
            modelRefBuildDirs = infoStruct.modelRefsBuildDirsAll;
            refModelsWithProfiling = setdiff...
                (infoStruct.allModelsWithCodeProfiling, infoStruct.modelName);
            sharedBinaryDir = infoStruct.sharedBinaryDir;
            relativePathToAnchor = infoStruct.relativePathToAnchor;
            pathToAnchor = fullfile(...
                codeDir,...
                relativePathToAnchor);
            % (g928770, g1434797) - improve API to manage profile_info.mat
            coder.profile.CoderInstrumentationInfo.updateExistingRegistries(...
                codeDir);
            this.CodeInstrumentationEnabled =...
                coder.profile.CoderInstrumentationInfo.isInstrumentationEnabled(...
                    codeDir,...
                    [modelRefBuildDirs, {sharedBinaryDir}],...
                    pathToAnchor,...
                    this, refModelsWithProfiling);
        end
        
        function lProfilingEnabled = initExecutionProfilingSettings(this)
            % Is execution profiling enabled
            lProfilingEnabled = strcmp...
                (this.getInstrumentationSettingsModelParam('CodeExecutionProfiling'), 'on');
            this.ProfilingEnabled = lProfilingEnabled && ~this.getSILPILInterface.IsForSldvAnalysis;

            if this.ProfilingEnabled
                this.CodeProfilingSaveOptions = this.getInstrumentationSettingsModelParam...
                    ('CodeProfilingSaveOptions');
                this.CodeExecutionProfileVariable = ...
                    this.getInstrumentationSettingsModelParam('CodeExecutionProfileVariable');                
            end 
        end
        
        function initTaskProfilingStreaming(this)
            if this.ProfilingEnabled && ...
                    ~isempty(this.getSILPILInterface)                
                % ignore the block path for top-model SIL/PIL
                % which uses a Model block within a wrapper model
                if ~this.getSILPILInterface.isTopModelXILSim
                    blockPath = this.getSILPILInterface.getBlockPath;
                    if ~isempty(blockPath)
                        appDir = this.getComponentArgs.getApplicationCodePath;
                        coder.profile.CoderInstrumentationInfo.setTaskRootBlockPath...
                            (appDir,...
                            blockPath);
                    end
                end
            end
        end
        
        % accessor for SILPILInterface
        function silPILInterface = getSILPILInterface(this)
           silPILInterface = this.SILPILInterface; 
        end
       
        function enabled = isCodeInstrumentationEnabled(this)
           enabled = this.CodeInstrumentationEnabled;
        end
       
        function setCodeInstrumentationEnabled(this, value)
            this.CodeInstrumentationEnabled = value;
        end                
        
        function enabled = isProfilingEnabled(this)
            enabled = this.ProfilingEnabled;
        end
        
        function lTimer = getProfilingTimer(this)
            
            % There must be a timer specification to support execution profiling
            targetServices = this.getTargetServices();
            lTimer = targetServices.getProfilingService().getTimer();
            if isempty(lTimer) && this.isProfilingEnabled
                rtw.pil.ProductInfo.error('pil', 'ConnectivityConfigNoTimer', ...
                                          targetServices.getName());
            end
            
        end

        function setProfilingHWCounterUnit (this, lHWCounterUnit)
            this.ProfilingHWCounterUnit = lHWCounterUnit;
        end
        
        function streamingData = getLoggingData(this)
            streamingData = [this.SignalLoggingData this.StateLoggingData];
        end                

        % validate non-parameter I/O data interfaces and collect a list of 
        % their datatypes
        function validateIODataInterfaces(this)            
           % build a list of data interfaces to check                                                              
           dataElements = [rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.getCodeInfo.Inports), 'input'), ...
                           rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.getCodeInfo.Outports), 'output'), ...
                           rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.CodeInfoUtils.getReaderSharedDataStores), 'dsread'), ...
                           rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.CodeInfoUtils.getWriterSharedDataStores), 'dswrite'), ...
                           rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.CodeInfoUtils.getInportSizes), 'inputsize'), ...
                           rtw.pil.CodeInfoData.createHostDataElements(this, 1:length(this.CodeInfoUtils.getOutportSizes), 'outputsize')];           
           % add command data
           for i=1:length(this.CommandData)
              dataElements = [dataElements this.CommandData(i).getHostDataElements];                                %#ok<AGROW>
           end           
           % validate non-parameters
           allIODataTypes = this.validateDataElements(dataElements);
           % add parameter io datatypes           
           allIODataTypes = [allIODataTypes this.ParamIODatatypes];           
           % add logging io datatypes
           allIODataTypes = [allIODataTypes this.LoggingIODataTypes];
           %           
           % make sure these warnings are displayed only once, 
           % during wrapper creation
           this.throwUnsupportedTunableParameterWarnings;                       
           % uniquify data type list and store
           this.AllIODataTypes = unique(allIODataTypes);
        end
        
        function allIODataTypes = getAllIODataTypes(this)
           allIODataTypes = this.AllIODataTypes; 
        end
                
        % return indices of tunable workspace parameters that are supported
        % by the SIL/PIL infrastructure
        function [tunableWorkspaceIndices, ...
                  tunableBaseWorkspaceIndices, ...
                  tunableModelWorkspaceIndices] ...
                = getSupportedTunableWorkspaceParams(this)            
            tunableWorkspaceIndices = this.SupportedTunableWorkspaceParams;
            tunableBaseWorkspaceIndices = this.SupportedTunableBaseWorkspaceParams;
            tunableModelWorkspaceIndices = this.SupportedTunableModelWorkspaceParams;
        end                        
        
        function isSupportedTunableWorkspaceParam = isSupportedTunableWorkspaceParam(this, dataInterface)
            index = find(arrayfun(@(x) isequal(x,dataInterface), ...
                this.getCodeInfo.Parameters(this.SupportedTunableWorkspaceParams)));
            assert(isempty(index) || isscalar(index), ...
                'Found multiple matching Parameters');
            if isempty(index)
                isSupportedTunableWorkspaceParam = false;
            else
                isSupportedTunableWorkspaceParam = true;
            end            
        end
        
        % look up CommandData
        function commandData = getCommandData(this, name)
            commandData = [];
            for i = 1:length(this.CommandData)
                currCommandData = this.CommandData(i);
                if strcmp(currCommandData.getName, name)
                    commandData = currCommandData;
                    break;
                end
            end
            assert(~isempty(commandData), 'Command data "%s" not found.', name);
        end
        
        function memUnitInfo = getMemUnitInfo(this)
            % return memUnitInfo
            memUnitInfo = this.MemUnitInfo;
        end          
        
        function isEnabled = isMatFileLoggingEnabled(this)
            isEnabled = strcmp(this.getParam('MatFileLogging'), 'on');
        end
        
        function modelLoggingInfoStruct = initLoggingData(this, cachedXILBlockModelLoggingInfo)    
            % initialize signal logging
            modelLoggingInfoStruct = this.initSignalLoggingData(cachedXILBlockModelLoggingInfo);
            % initialize state logging
            offset = length(this.SignalLoggingData);
            this.initStateLoggingData(offset);
        end
        
        function initStateLoggingData(this, offset)
            supportedDataInterfaces = [];
            if this.getClientInterface.isStateLoggingEnabled(this, ...
                    this.getSILPILInterface.getInTheLoopType)
                % find all states
                [dworks, stateInfo] = this.getCodeInfoUtils.getLoggedStates();
                % initialize states that can be logged
                inTheLoopType = this.getSILPILInterface.getInTheLoopType;
                [this.StateLoggingData, supportedDataInterfaces] = ...
                    this.getClientInterface.initializeStateLoggingData(this, ...
                    dworks, stateInfo, offset, inTheLoopType);
            end
            this.CodeInfoUtils.setStateLogs(supportedDataInterfaces, this.StateLoggingData);
        end
        
        function modelLoggingInfoStruct = initSignalLoggingData(this, cachedXILBlockModelLoggingInfo)    
            % initialize to empty in case logging is not enabled
            modelLoggingInfoStruct = [];             
            loggingData = [];            
            % build list of supported data interfaces to pass to
            % CodeInfoUtils
            supportedDataInterfaces = [];
            % build list of data types needed for logging
            loggingIODataTypes = {};            
            inTheLoopType = this.getSILPILInterface.getInTheLoopType;
            if this.getClientInterface.isSignalLoggingEnabled(this, inTheLoopType)            
                % initialize for logging
                this.initSignalLogging;
                [loggingData, modelLoggingInfoStruct, supportedDataInterfaces, loggingIODataTypes] = ...
                    this.getClientInterface.initializeSignalLoggingData(this, ...
                        this.SigLogData, cachedXILBlockModelLoggingInfo, ...
                        inTheLoopType, this.SigLogInfo);
            end            
            % apply to object properties
            this.SignalLoggingData = loggingData;            
            % initialize CodeInfoUtils with the supported list of data
            % interfaces
            %
            % Note: currently, we do not reset SigLogs from within
            % "resetCodeInfoUtils" - it is not necessary.
            this.CodeInfoUtils.setSigLogs(supportedDataInterfaces);
            % reset the list of logging data types
            this.LoggingIODataTypes = loggingIODataTypes;
        end
        
        % API_getDatatypeSize: Method to return the target datatype size for
        %                  standard Simulink datatypes.
        %
        % size = API_getDatatypeSize(simulinkType)
        %
        % simulinkType: A Simulink datatype name.
        %
        % size: The corresponding target datatype size in 8-bit bytes
        function size = API_getDatatypeSize(this, simulinkType)
            % protected call through API
            try
                size = getDatatypeSize(getExtendedHardwareConfig(this.getConfig), ...
                    simulinkType);
            catch userException
                rtw.pil.SILPILInterface.throwImplementationError(userException);
            end
        end   
        
        % (re)initialize CodeInfo and CodeInfoUtils
        %
        % Note: SILPILWrapperUtils (and subclasses) must avoid caching 
        % handles of objects contained within CodeInfo & CodeInfoUtils 
        % otherwise they will become out of date after calls to this 
        % method.
        function resetCodeInfoUtils(this)          
            
            codeInfoPath = this.getSILPILInterface.getCodeInfoPath;
            % create new codeInfoUtils, pass the ValidateCodeInfoUtils flag
            % to avoid running the validation every time we call 
            % resetCodeInfoUtils (i.e. Sfunction creation, xil_interface
            % creation and when restoring access to codedescriptor from a
            % cached SILPILInterface)
            this.CodeInfoUtils = this.getSILPILInterface.getCodeInterfaceFactory.createCodeInfoUtils(...
                codeInfoPath, ...
                this.getClientInterface.createCoreClientInterface,...
                this.getConfigInterface, ...
                this.getComponentForDiagnostics, ...
                this.ValidateCodeInfoUtils);
            
            % reset the cache 
            this.CodeInfoDataCache = containers.Map;
        end            

        function isEnabled = isCPPEncap(this)
            isEnabled = strcmpi(this.getParam('IsCPPClassGenMode'),'on');
        end  
        
        % Return the instrumentation settings model.
        function instrumentationSettingsModel = getInstrumentationSettingsModel(this)            
            % use root model
            instrumentationSettingsModel = this.SILPILInterface.getRootModel;
        end  
        
        % accessor for instrumentation settings ConfigSet params
        function value = getInstrumentationSettingsModelParam(this, param)           
            % In the SIL/PIL block case we use cached config set because the original top
            % model may no longer be open.
            value = this.getParam(param);
        end                

        % accessor for instrumentation settings model build hooks.
        function hooks = getInstrumentationSettingsModelBuildHooks(this)            
            hooks = this.getInstrumentationSettingsModelParam('RTWBuildHooks');                        
            if this.getSILPILInterface.getInTheLoopType==rtw.pil.InTheLoopType.Block
                % Normally, the definitive source of code coverage settings is the named
                % parameter, CodeCoverageSettings passed into
                % slbuild_private. However, for a SIL/PIL block simulation, code
                % coverage is not allowed and we must switch off any build hooks
                % associated with code coverage.
                lCodeCoverageSettings = [];
                hooks = coder.coverage.updateBuildHooks(lCodeCoverageSettings, hooks);
            elseif this.getClientInterface().isMWCodeCoverageEnabled()
                %  Add the Simulink Coverage hook (if it doesn't already exist).
                codeCovSettings = slprivate('getCodeCoverageSettings', @(name) this.getInstrumentationSettingsModelParam(name));
                if strcmp(codeCovSettings.CoverageTool, SlCov.getCoverageToolName())
                    hooks = coder.coverage.updateBuildHooks(codeCovSettings, hooks);
                end
            end
        end
        
        % return whether we should obfuscate the SIL/PIL wrapper files.
        % Class ModlRefSILPILWrapperUtils overrides this implementation.
        function obfuscateCode = shouldObfuscateXILWrapperFiles(this) %#ok<MANU>
            obfuscateCode = false;            
        end
            
            
        % validate an I/O data interface and collect a list of its datatypes        
        function allIODatatypes = validateDataElements(this, dataElements)
            allIODatatypes = {};
            for dataElementIdx = 1:length(dataElements)
                cData = dataElements(dataElementIdx);
                cData.validate(this);                                              
                % add the ioDatatype
                allIODatatypes{end+1} = cData.SimulinkType; %#ok<AGROW>
            end
        end
        
        % return the "base" storage implementation for a root implementation
        function storageImplementation = getBaseStorageImplementation(this, implementation)
            implementationClass = this.getCodeInfoUtils.class(implementation);

            if isempty(implementation)
                %If implementation empty return empty
                storageImplementation = [];
                return;
            end

            switch implementationClass
                case {'PointerVariable'}
                    % storage is the target variable
                    storageImplementation = implementation.TargetVariable;
                    % prepend an extra tag to the identifier to further de-risk name
                    % clashes
                    tag = 'tgt_var_';
                    storageImplementation.Identifier = [tag storageImplementation.Identifier];
                case {'ArrayExpression'}
                    % storage is the base region
                    storageImplementation = implementation.BaseRegion;
                case {'StructExpression', ...
                        'PointerExpression', ...
                        'ClassMemberExpression', ...
                        'StaticMemberExpression'}
                    % recursively process structures to get to the base
                    % region
                    storageImplementation = this.getBaseStorageImplementation(implementation.BaseRegion);    
                case 'AutosarCalibration'
                    baseRegion = this.getCodeInfoUtils.getAutosarCalibrationBaseRegion(implementation);
                    storageImplementation = this.getBaseStorageImplementation(baseRegion);                    
                case {'Variable' ...
                        'CustomExpression' ...
                        'ClassMethodExpression', ...
                        'StructAccessorVariable',...
                        'BasicAccessFunctionExpression', ...
                        'CustomVariable', ...
                        'AutosarMemoryExpression'}
                    storageImplementation = implementation;
                case {'Literal'}
                    storageImplementation = [];
                case {'TypedCollection'}
                    storageImplementation =this.getCodeInfoUtils.getArray(implementation.Elements);
                otherwise
                    % unknown / unexpected case
                    assert(false, 'Unexpected implementation class: %s', ...
                        implementationClass);
            end
        end
        
        % Return the inport or outport, depending on the value of the
        % 'input' parameter, of index 'idx' from infoStruct.modelInterface.
        % Only real input/output ports are considered: 'function-call'
        % ports are ignored.
        function ioPort = getModelInterfaceIOPort(this,...
                input, idx)
            ioPort = this.getIOPortByIndex(input, idx,...
                this.getInfoStruct.modelInterface);
        end

        % test for a legacy AUTOSAR client block
        function retVal = isServerFunctionFromOldAUTOSARClientBlock(this, svrFcnName)
            retVal = any(strcmp(svrFcnName,...
                this.getInfoStruct.AUTOSARClientBlockOperationNames));
        end
        
        % return function info, unless suppressed by needToProcessServerFcns
        function serverFunctionsInfo = getServerFunctionsInfo(this)
            if this.needToProcessServerFcns
                serverFunctionsInfo = this.getCodeInfoUtils.getServerFunctionsInfo;
            else
                serverFunctionsInfo = [];
            end
        end
        
        function needToProcessServerFcns = needToProcessServerFcns(this)
            % process server fcns except for the SIL/PIL block and 
            % Top-model SIL/PIL which do not allow them to be called 
            % externally
            if (this.SILPILInterface.getInTheLoopType == rtw.pil.InTheLoopType.Block) || ...
                    this.SILPILInterface.isTopModelXILSim
                needToProcessServerFcns = false;
            else
                needToProcessServerFcns = this.getCodeInfoUtils.hasServerFunctions;
            end
        end
        
        function dataStoresToTransfer = getDataStoresToTransferIndices(this, allIndices, dsmType)
            %To support shared local data stores need an abstraction that
            %filters out shared local data stores from list of shared data
            %stores when we don't want to transfer them to/from the host.
            %Only the model block can properly handle shared local data
            %stores, so only transfer them with the model block Sfunction
            switch dsmType
                case 'dsread'
                    dataInterfaces = this.getCodeInfoUtils.getReaderSharedDataStores(allIndices);
                case 'dswrite'
                    dataInterfaces = this.getCodeInfoUtils.getWriterSharedDataStores(allIndices);
                case 'initializeSharedLocal'
                    %Special INITIALIZE time only, operating on all Shared
                    %Local DSM
                    dataInterfaces = [];
                otherwise
                    assert(false, 'Invalid dsmType passed');
            end
            dataStoresToTransfer = [];
            for idx=1:numel(dataInterfaces)
                %Include only Global DSMs
                if isempty(dataInterfaces(idx).SharedLocalDSMInfo)
                    dataStoresToTransfer(end+1) = allIndices(idx); %#ok<AGROW>
                end
            end
        end
        
        function dataStoresToTransfer = getDataStoresToTransferInterfaces(this, allInterfaces)
            dataStoresToTransfer = this.CodeInfoUtils.getEmptyInterface('ReadWriteDataInterface');
            for idx=1:numel(allInterfaces)
                %Include only Global DSMs
                if isempty(allInterfaces(idx).SharedLocalDSMInfo)
                    dataStoresToTransfer(end+1) = allInterfaces(idx); %#ok<AGROW>
                end
            end
        end
        
        function isModelReferenceTarget = isModelReferenceTarget(this)
            isModelReferenceTarget = ...
                (this.getSILPILInterface.getInTheLoopType == rtw.pil.InTheLoopType.ModelBlock);
        end
        
        function runDeferredValidation(~)
            % Overridden by subclass methods
        end
        
        function restoreAccessToCodeDescriptorObjs(this)
            % make sure mf0 objects are available if the handle to
            % CodeDescriptor was deleted
            if ~this.CodeInfoUtils.hasValidCodeDescriptorHandle
                % get access to the component interface
                this.resetCodeInfoUtils;
                % get access to the command data
                this.initCommandData;
            end
        end
        
        function name = getLoadableRootComponent(this)
            % return the name of the Root Component
            name = this.getSILPILInterface.getRootModelAndSystemPath;
        end
        
        function name = getComponentForSfuncName(this)
            % return the name of the component after which the Sfunction is
            % going to be named
            name = this.getCodeInfoUtils.GraphicalPath;
        end
        
        function name = getComponentForDiagnostics(this)
            % return the name of the Root Component for reporting in
            % diagnostic messages
            name = this.getSILPILInterface.getRootModelAndSystemPath;
        end
        
        function isReadOnly = isComponentReadOnly(this)
            % by default, return readOnly if the component under test is a
            % protected model
            isReadOnly = this.isModelProtected;
        end
        
        function isProtected = isModelProtected(this)
            isProtected = this.SILPILInterface.IsModelProtected;
        end
    end
       
    methods (Hidden)
        function enabled = isSlCovEnabled(this)
            enabled = false;
            if this.getClientInterface().isMWCodeCoverageEnabled()
                switch (this.SILPILInterface.getInTheLoopType)
                  case { rtw.pil.InTheLoopType.ModelBlock, ...
                         rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                         rtw.pil.InTheLoopType.Block}
                    covSettings = slprivate('getCodeCoverageSettings', @(name) this.getInstrumentationSettingsModelParam(name));
                    if strcmp(covSettings.CoverageTool, SlCov.getCoverageToolName())
                        instrumentationSettingsModel = this.getInstrumentationSettingsModel;
                        modelName = this.SILPILInterface.getRootModel();
                        refModelsList = find_mdlrefs(modelName);
                        for ii = 1:numel(refModelsList)
                            if SlCov.CodeCovUtils.isXILCoverageEnabled(instrumentationSettingsModel, refModelsList{ii}, this.SILPILInterface.isSIL())
                                enabled = true;
                                return
                            end
                        end
                    end
                end
            end
        end
    end
    
    methods (Access = 'protected')                        
        
        function matFileName = getCAPIMatFileName(this)         %#ok<MANU>
            matFileName = 'capi.mat';
        end                                                                           
        
        % define hook method for subclasses to implement
        % use to validate non-workspace parameters
        function paramIODatatypes = validateTunableNonWorkspaceParametersHook(this)             %#ok<MANU>
            paramIODatatypes = [];
        end
        
        % Does this flavor support tunable workspace parameters?
        function supports = supportsModelWorkspaceParams(this) %#ok
            supports = true;
        end

        function indices = getInputPortIndices( this, inportIndices, withDF )
            % Filter out local_inports.
            % local_inports can be a single matrix or a cellarray,
            % depending by its cardinality
            if (isempty(inportIndices))
                indices = [];
            elseif (isempty(this.InportDirectFeedThrough))
                indices = inportIndices; % Cover the mex target case
            else
                
                hasDF = @(x)this.InportDirectFeedThrough(x);
                indices = double(inportIndices(~xor(withDF,arrayfun(hasDF,inportIndices))));
            end
        end
        
        function validateIndividualPIMMapping(~)
            % this method is overwritten by child classes as needed
        end
        
        function validateModelArguments(~)
            % this method is overwritten by child classes as needed
        end
        
        function validateExportedParameters(~)
            % this method is overwritten by child classes as needed
        end
    end
    
    methods (Access = 'protected', Abstract)
       warnForUnsupportedTunableParameters = warnForUnsupportedTunableParameters(this);        
    end                       
    
    methods (Access = 'private')
        
        function validateConfigInterface(this)
            % validate configuration interface, this validation does not
            % require CodeInfoUtils and should ran as early as possible
            
            if (this.isValidParam('ExplicitPartitioning') && ...
                    strcmp(this.getParam('ExplicitPartitioning'), 'on'))
                rtw.pil.ProductInfo.error('pil', 'ConcurrentExecutionSILPILBlock', ...
                    this.getComponentForDiagnostics);
            end
            
            % Classic Call Interface is not supported because it does
            % not generate codeInfo and does not populate codeDescriptor
            if (this.isValidParam('GRTInterface') && ...
                    strcmp(this.getParam('GRTInterface'), 'on'))
                rtw.pil.ProductInfo.error('pil', 'UnsupportedGRTInterface');
            end
            
            inTheLoopType = this.getSILPILInterface.getInTheLoopType;
            if inTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn
                % Code Coverage is not currently supported with GRT targets
                if (this.IsGRTBased && ...
                        strcmp(this.getParam('CovEnable'), 'on'))
                    rtw.pil.ProductInfo.error('pil', 'pilGRTCodeCoverage', ...
                        this.getComponentForDiagnostics);
                end
                covSettings = this.getParam('CodeCoverageSettings');
                if (this.IsGRTBased && ...
                        ~strcmp(covSettings.CoverageTool, 'None'))
                    rtw.pil.ProductInfo.error('pil', 'pilGRTCovSettings', ...
                        this.getComponentForDiagnostics);
                end
            end
            
             if (inTheLoopType==rtw.pil.InTheLoopType.Block) || ...
                  (inTheLoopType==rtw.pil.InTheLoopType.ModelBlockStandalone)
              
              % check for unsupported variable-size I/O signals
              infoStruct = this.getInfoStruct;
                if isfield(infoStruct.modelInterface, 'Inports')
                    inports = infoStruct.modelInterface.Inports;
                    for inportIdx = 1:length(inports)
                        if iscell(inports)
                            inport = inports{inportIdx};
                        else
                            inport = inports(inportIdx);
                        end
                        if inport.IsVarDim && (slfeature('slcgIgnoreUpperBounds')~=1)
                            DAStudio.error('PIL:pil:XILBlockVariableSizeSignals', ...
                                'Inport', ...
                                int2str(inportIdx), ...
                                this.getComponentForDiagnostics);
                        end
                    end
                end
                if isfield(infoStruct.modelInterface, 'Outports')
                    outports = infoStruct.modelInterface.Outports;
                    for outportIdx = 1:length(outports)
                        if iscell(outports)
                            outport = outports{outportIdx};
                        else
                            outport = outports(outportIdx);
                        end
                        if outport.IsVarDim && (slfeature('slcgIgnoreUpperBounds')~=1)
                            DAStudio.error('PIL:pil:XILBlockVariableSizeSignals', ...
                                'Outport', ...
                                int2str(outportIdx), ...
                                this.getComponentForDiagnostics);
                        end
                    end
                end
                if strcmp(this.getParam('GenerateAllocFcn'), 'on')
                    if strcmp(this.getParam('RootIOFormat'), 'Part of model data structure')
                        %G1051903 - SIL/PIL Cannot currently support the
                        %Allocation function, when Root IO is packed into model
                        %data structure, because CodeInfo doesn't correctly
                        %capture the association of model arguments to the
                        %Model Structure (See G1056346)
                        
                        DAStudio.error('PIL:pil:AllocFunctionWithRootIOPackedInRTM');
                    elseif this.IsGRTBased
                        DAStudio.error('PIL:pil:AllocFunctionWithGRT');
                    end
                end
            end
        end
        
        
        function validateCodeInterface(this)
            % validate code interface, this validation requires 
            % CodeInfoUtils and should ran only after it has been
            % initialized
            
            this.validateModelArguments;
            this.validateInstPCodeDescrInterface;
            
            inTheLoopType = this.getSILPILInterface.getInTheLoopType;
            if inTheLoopType==rtw.pil.InTheLoopType.Block
                assert(~this.getCodeInfoUtils.isExportFcnDiagram, ...
                    'No XIL block support for model export functions.');                
                % get all undefined callers
                callerFcnsInfo = this.getCodeInfoUtils.getUndefinedCallerFunctionsInfo;
                for callerIdx = 1:length(callerFcnsInfo)
                   callerFunctionInfo = callerFcnsInfo(callerIdx);
                   opName = callerFunctionInfo.getOperationName;
                   if isa(callerFunctionInfo, 'rtw.pil.QueuedIOFunctionInfo')
                       %Skip Queued Message Caller functions
                       continue;
                   end
                   if ~this.isServerFunctionFromOldAUTOSARClientBlock(opName)
                      % no support for undefined callers, except for the
                      % legacy AUTOSAR client block                      
                      blocks = callerFunctionInfo.RefBlockPaths;
                      % choose the first block
                      block = blocks{1};                      
                      blockName = regexp(block,'(.+)[^|]','match');
                      DAStudio.error('PIL:pil:XILBlockNotSupportedForUndefinedCallers', ...
                          opName, blockName{1});               
                   end                    
                end                                
            end            
            
            if this.isValidParam('ConcurrentTasks') && ...
                    strcmp(this.getParam('ConcurrentTasks'), 'on')
                if this.getCodeInfoUtils.isMultiRate
                    rtw.pil.ProductInfo.error('pil', 'ConcurrentTasks', ...
                        this.SILPILInterface.getRootModel);
                elseif (strcmp(this.getParam('ExplicitPartitioning'),'off') && ...
                        strcmp(this.getParam('TargetOS'), 'NativeThreadsExample'))
                    if  ~(coder.profile.private.multicoreFeatureOn && ...
                        strcmp(this.getParam('AutotuneMulticoreCodegen'), 'on'))
                        rtw.pil.ProductInfo.error('pil', 'ConcurrentTasksImplicitPartitioning', ...
                            this.SILPILInterface.getRootModel);
                    end
                end
            end                                    
            
            % Check the global data stores in the system.
            if inTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn && ~this.IsUpdateModelReferenceTargets
                this.getClientInterface.validateGlobalDataStores(this, inTheLoopType, ...
                    this.SILPILInterface, this.getCodeInfoUtils.getGlobalDataStores);
            end

            % The Async Task Specification block has to be connected to a root
            % level Inport so we don't need to worry about right-click
            % export functions.   However, we do need to error for
            % top-model SIL/PIL block builds of async fcn call models.            
            isXILBlockAsyncFcnCallModel = (inTheLoopType==rtw.pil.InTheLoopType.Block) && ...
                this.CodeInfoUtils.hasExpInportsSpecification && ...
                ~this.SILPILInterface.isSubsystemComponent;
            
            % Detect "AsyncFcnCallModelTopLevelBuild"
            % (i.e. async fcn calls with task specification blocks)
            % with top-model SIL/PIL
            % 
            % In future we could potentially support this case by adding task 
            % specification blocks inside the harness model.
            isTopModelXILSim = this.getSILPILInterface.isTopModelXILSim;
            isTopModelAsyncFcnCallModel = isTopModelXILSim && ...
                    this.CodeInfoUtils.hasExpInportsSpecification && ...
                    ~this.CodeInfoUtils.isExportFcnDiagram;

            if isXILBlockAsyncFcnCallModel || isTopModelAsyncFcnCallModel                
                % This is currently only supported for Model block SIL/PIL
                % with "Code interface" set to "Top model".   
                model = this.getComponentForDiagnostics;
                DAStudio.error('PIL:pil:TopModelTaskSpecNotSupported', ...
                    model, model);
            end

            if inTheLoopType==rtw.pil.InTheLoopType.Block
                dataInterfaces = [this.getCodeInfo.Inports, this.getCodeInfo.Outports];
                for idx = 1:numel(dataInterfaces)
                    if isprop(dataInterfaces(idx),'VariantInfo') && ~isempty(dataInterfaces(idx).VariantInfo)
                        % SIL block doesn't support Inline variants
                        DAStudio.error('PIL:pil:InlineVariantNotSupported', ...
                            this.getClientInterface.createCoreClientInterface.getDisplayStringFromSID(dataInterfaces(idx).SID));
                    end
                end
                %Throw an error if we have DSM Writes in Initialize
                %Functions. See g1530233
                if this.getCodeInfoUtils.hasDSMWritesInInit
                    model = this.getComponentForDiagnostics;
                    DAStudio.error('PIL:pil:SILPILBlockDSMWriteInInit', model);
                end
                
            end
            if inTheLoopType==rtw.pil.InTheLoopType.Block
                if ~isempty(this.CodeInfoUtils.getSharedLocalDataStores)
                    model = this.getComponentForDiagnostics;
                    DAStudio.error('PIL:pil:PILBlockSharedLocalDSMS', model);
                end
            end
            if this.getSILPILInterface.isModelBlockXIL && ~isTopModelXILSim
                allSharedLocalDSMs = this.CodeInfoUtils.getSharedLocalDataStores;
                [~, dispString] = this.CodeInfoUtils.checkForEmptyImplementations(allSharedLocalDSMs, 'DataStore');                                
                if ~isempty(dispString)
                    % There were shared local data stores with empty
                    % implementations. Throw a special error for these.
                    DAStudio.error('Connectivity:target:SharedLocalDataStoreUnsupportedStorageClass', dispString);                
                end
            end            
            
            this.validateIndividualPIMMapping;
        end
        
        function y = isOutputUpdateRequired(this)
            % Check if output / update function processing is required.
            % If this is export functions and sample time independent
            % then output / update processing is not required.
            y = ~(this.CodeInfoUtils.isExportFunctions &&...
                this.CodeInfoUtils.isSampleTimeIndependent);
        end
        
        function componentArgs = createComponentArgs(this, ...
                lCodeExecutionProfilingEnabled, lXilCompInfo)
            % see isUpToDate to see when ComponentArgs & Connectivity
            % Config is refreshed.
            
            [componentCodePath, componentCodeName, applicationCodePath] = ...
                this.getSILPILInterface.getCodeDirsForComponentArgs;                       

            % make sure tgt dir exists
            if ~exist(applicationCodePath, 'dir')
                mkdir(applicationCodePath);
            end
            
            % create the componentArgs object
            componentArgs = this.getClientInterface.createComponentArgs(this.getLoadableRootComponent, ...
                                                              componentCodePath, ...
                                                              componentCodeName, ...
                                                              applicationCodePath, ...
                                                              this.getSILPILInterface, ...
                                                              this.getInstrumentationSettingsModel,...
                                                              this.getInstrumentationSettingsModelBuildHooks(),...
                                                              lCodeExecutionProfilingEnabled);
            internalData.targetServices = this.getTargetServices();
            internalData.configInterface = this.getConfigInterface;            
            internalData.IsSIL = this.getSILPILInterface().isSIL();
            componentArgs.setInternalData(internalData);       
            componentArgs.setXilCompInfo(lXilCompInfo);
            componentArgs.setCoderAssumptionsAnchorDir(this.getSILPILInterface().CodeDescriptorDir);
            
            % Currently verbosity for the connectivity output is set from
            % the rtw verbosity. This can be overridden by the connectivity
            % config communicator. We could offer this as an explicit
            % setting in the verifications settings in the future.
            isVerbose = isequal(this.getConfigInterface().getParam('RTWVerbose'), 'on');
            componentArgs.setVerboseCommunicationOutput(isVerbose);
            componentArgs.setClientInterface(this.getClientInterface);
        end                            
        
        % issue cached warnings associated with unsupported tunable params
        function throwUnsupportedTunableParameterWarnings(this)
            for warnInfoIdx = 1:length(this.UnsupportedTunableParameterWarningInfo)
                warnInfo = this.UnsupportedTunableParameterWarningInfo(warnInfoIdx);
                diString = this.CodeInfoUtils.getDataInterfaceString(...
                              this.getCodeInfo.Parameters(warnInfo.ParamIdx), ...
                              'Parameter');
                rtw.pil.ProductInfo.warning('pil', ...
                    'UnsupportedTunableParameter', ...
                    diString, ...
                    warnInfo.Exception.message);
            end
        end                                             
        
        function validateParameters(this)
            % validate tunable params - this routine may define the
            % behaviour of getSupportedTunableWorkspaceParams (e.g. for
            % standalone code interface)
            this.ParamIODatatypes = this.validateTunableParameters;
            
            % validate exported parameters support
            this.validateExportedParameters;
        end
        
        % determine which tunable workspace parameters are supported by the
        % SIL/PIL infrastructure and collect a list of their datatypes
        function paramIODatatypes = validateTunableParameters(this)
            % handle workspace parameters
            paramIODatatypes = this.validateTunableWorkspaceParameters;
            % handle other types of parameters
            paramIODatatypes = [paramIODatatypes this.validateTunableNonWorkspaceParametersHook];
        end                                
        
        function paramIODatatypes = validateTunableWorkspaceParameters(this)
            % initialize our list of supported tunable params
            this.SupportedTunableWorkspaceParams = [];
            % initialize our list of warning info
            this.UnsupportedTunableParameterWarningInfo = [];
            % start with CodeInfoUtils workspace params
            paramIndices = this.CodeInfoUtils.getWorkspaceParams;
            paramIODatatypes = {};
            for paramIdx=paramIndices
                param = this.getCodeInfo.Parameters(paramIdx);  
                % check for empty implementations:
                if isempty(param.Implementation) || ~this.CodeInfoUtils.isDataInterfaceAccessible(param)
                    diString = this.CodeInfoUtils.getDataInterfaceString(param, ...
                                                                         'Parameter');  
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                                                'EmptyDataInterface', ...
                                                diString);                    
                    e = MException(id, message);
                    this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                    this.UnsupportedTunableParameterWarningInfo(end).Exception = e;    
                    % skip to next parameter
                    continue;
                end
                
                % Skip SystemConstants (empty type implies SystemConstant)
                if this.CodeInfoUtils.isDataInterfaceSystemConstant(param)
                    continue;
                end
                
                % Skip model workspace parameters if they are not supported
                if ~this.supportsModelWorkspaceParams() && ...
                        this.CodeInfoUtils.isModelWorkspaceParam(param)
                    if this.CodeInfoUtils.modelWorkspaceParametersCanBeUninitialized
                        % If the parameter might be uninitialized, add a 
                        % message stating that this mode of XIL
                        % does not support tuning model workspace parameters
                        [id, message] = rtw.pil.ProductInfo.message('pil', ...
                                                    'ModelWorkspaceNotSupported');                    
                        e = MException(id, message);                        
                        this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                        this.UnsupportedTunableParameterWarningInfo(end).Exception = e;    
                    end
                    continue;
                end
                
                % always warn for read only / static parameters                                
                isReadOnly = this.CodeInfoUtils.isDataInterfaceReadOnly(param);
                if isReadOnly                    
                    diString = this.CodeInfoUtils.getDataInterfaceString(param, ...
                                                                         'Parameter');  
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                                                'ReadOnlyDataInterface', ...
                                                diString);                    
                    e = MException(id, message);
                    this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                    this.UnsupportedTunableParameterWarningInfo(end).Exception = e;    
                    % skip to next parameter
                    continue;
                end
                
                %Skip parameters with variants, unless they are imported.
                variantCondition = ...
                    this.CodeInfoUtils.getCodeVariant(param);
                if ~isempty(variantCondition) && param.Implementation.isDefined
                    % skip to next parameter
                    continue
                end
                isStatic = this.CodeInfoUtils.isDataInterfaceStatic(param);
                if isStatic
                    diString = this.CodeInfoUtils.getDataInterfaceString(param, 'Parameter');  
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                                                'StaticDataInterface', ...
                                                diString);                    
                    e = MException(id, message);
                    this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                    this.UnsupportedTunableParameterWarningInfo(end).Exception = e;    
                    % skip to next parameter
                    continue;
                end                                
                try
                    % create dataElements for validateDataElements -
                    % dataElements
                    % contains contiguous elements of the current
                    % parameter
                    dataElements = rtw.pil.CodeInfoData.createHostDataElements(this, paramIdx, 'parameter');
                    newParamIODatatypes = this.validateDataElements(dataElements);
                    
                    % update paramIODatatypes
                    paramIODatatypes = [paramIODatatypes newParamIODatatypes]; %#ok<AGROW>
                    % store paramIdx
                    this.SupportedTunableWorkspaceParams = [this.SupportedTunableWorkspaceParams paramIdx];
                    if this.CodeInfoUtils.isModelWorkspaceParam(param)
                        this.SupportedTunableModelWorkspaceParams = ...
                            [this.SupportedTunableModelWorkspaceParams paramIdx];  
                        % build up a string representation suitable for an
                        % S-Function parameter string
                        this.SupportedTunableModelWorkspaceParamsString = ...
                            strjoin([this.SupportedTunableModelWorkspaceParamsString, ...
                            {param.GraphicalName}], ...
                            ', ');
                    else
                        this.SupportedTunableBaseWorkspaceParams = ...
                            [this.SupportedTunableBaseWorkspaceParams paramIdx];
                    end
                catch e
                    % validation failed
                    if this.warnForUnsupportedTunableParameters
                        this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                        this.UnsupportedTunableParameterWarningInfo(end).Exception = e;    
                    else
                        rethrow(e);
                    end
                end
            end
        end                                                          
        
        function initSignalLogging(this)
            if this.getCodeInfoUtils.UseMF0Logging
                [this.SigLogData, this.SigLogInfo] = ...
                    this.getCodeInfoUtils.getLoggedSignals(...
                        this.getSILPILInterface.getInTheLoopType);
            else
                % path to capi.mat
                slprjInternalPath = this.SILPILInterface.getSlprjInternalPath;
                matFileName = this.getCAPIMatFileName;
                capiPath = fullfile(slprjInternalPath, matFileName);
                assert(exist(capiPath, 'file') == 2, ...
                    'C API MAT file "%s" does not exist!', ...
                    capiPath);
                capiStruct = load(capiPath);
                this.SigLogData = this.CodeInfoUtils.initCAPIData(capiStruct);
            end
        end
        
       % initialize the command data information
       %
       % the command data is info associated with commands that can be
       % transferred between the host and target
       %
       % in addition to data such as TID and FCNID, command data could also
       % be used to transfer extra information between the host and
       % target such as taskCounter, rateTransition, clock ticks etc. This
       % extra information may be command specific or required for every
       % command (see rtw.pil.Sfunction.getPILIOCommandDataToProcess).
       %
       % On the target, this data may be explicitly accessed
       % in pil_interface_lib.c or may be mapped to global
       % variables via a command data I/O table generated into
       % pil_interface.c (this concept is not yet supported in the
       % TargetInterface classes)
       function initCommandData(this)
           % Note: we should only use datatypes that we expect to be defined
           % on the target (e.g. uint32) - if we ever need to support an
           % architecture without a 32-bit type then we may have to determine
           % the datatypes used in the command data dynamically based on the target.
           this.CommandData = rtw.pil.CommandData.empty;
           mf0ModelInstance = this.CodeInfoUtils.getMF0ModelInstance;
           this.CommandData(end+1) = rtw.pil.CommandData('FCNID', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt32, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('FCNID_AND_TID', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt32, ...
               2, mf0ModelInstance);
                      
           this.CommandData(end+1) = rtw.pil.CommandData('PRINTF_ERROR', ...
               this.getMemUnitInfo.memUnitType, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('PRINTF_SIZE', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt16, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('SERVER_FUNCTION_ID', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt32, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('COMMAND_COMPLETE', ...
               this.getMemUnitInfo.memUnitType, ...
               1, mf0ModelInstance);
                     
           this.CommandData(end+1) = rtw.pil.CommandData('FOPEN_FID', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt16, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('FOPEN_NAME_SIZE', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt16, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('FPRINTF_ERROR', ...
               this.getMemUnitInfo.memUnitType, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('FPRINTF_FID', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt16, ...
               1, mf0ModelInstance);
           
           this.CommandData(end+1) = rtw.pil.CommandData('FPRINTF_SIZE', ...
               rtw.connectivity.CodeInfoTypeEnum.TypeUInt16, ...
               1, mf0ModelInstance);               
       end
       
       function initMemUnitInfo(this)
           %INITMEMUNITINFO Initializes a struct containing information about memory units on the target
           %
           %   Return values:
           %       memUnitInfo: Memory unit info struct
           %                      
           
           narginchk(1, 1);
           
           % determine if this target is byte addressable or word addressable
           dataTypeService = this.getTargetServices().getDataTypeService();
           addressGranularity = dataTypeService.getAddressGranularity();
           memUnitInfo.memUnitType = rtw.connectivity.CodeInfoTypeUtils.getCodeInfoDTypeIdFromMatlabType(sprintf('uint%d',addressGranularity));
           
           % memUnitBytes is derived from memUnitType
           memUnitInfo.memUnitBytes = length(typecast(cast(1, rtw.connectivity.TypeUtils.codeInfoTypeIdToType(memUnitInfo.memUnitType)), 'uint8'));           
                               
           supportsOnlyByteIO = this.getTargetServices().getRtIOStreamApplicationService().getRtIOStreamInstance().getIsByteInputOutputOnly();
           if supportsOnlyByteIO
              % byte I/O
              % present data as bytes - for word addressable targets, 
              % words will have byte order of MATLAB host computer   
              memUnitInfo.ioDataTypeMATLAB = 'uint8';
              memUnitInfo.ioDataClass = 'mxUINT8_CLASS';
              memUnitInfo.ioDataType = 'uint8_T';              
           else
              % mem unit I/O 
              % present data as mem units
              memUnitInfo.ioDataTypeMATLAB = rtw.connectivity.TypeUtils.codeInfoTypeIdToType(memUnitInfo.memUnitType);
              memUnitInfo.ioDataClass = rtw.connectivity.TypeUtils.builtinToMxClassId(memUnitInfo.memUnitType);
              memUnitInfo.ioDataType = rtw.connectivity.TypeUtils.builtinToHostCType(memUnitInfo.memUnitType);                                              
           end      
           memUnitInfo.supportsOnlyByteIO = supportsOnlyByteIO;           
           % store memUnitInfo
           this.MemUnitInfo = memUnitInfo;
       end
       
       function initRtIOStreamHooks(this)
           clientInterface = this.ClientInterface;
           rtIOStream = this.getTargetServices().getRtIOStreamApplicationService().getRtIOStreamInstance();       
           isSIL = this.getSILPILInterface().IsSIL;
           
           rtIOStream.addPreOpenHook(@() clientInterface.rtIOStreamPreOpen(rtIOStream, isSIL));
       end
       
        % Get a cell array containing all inports or outports, depending on
        % the value of the 'input' parameter, from any structure with
        % Inports and Outports elements (for example,
        % this.getInfoStruct.modelInterface or
        % this.getInfoStruct.GeneralDataFromTLC)
        function cellports = getPortsAsCellArray(~, input, parentStruct)
            if input
                ports = parentStruct.Inports;
            else
                ports = parentStruct.Outports;
            end
            
            % Always produce a cell array
            if isscalar(ports)
                cellports{1} = ports;
            else
                cellports = ports;
            end
        end

        function ioPort = getIOPortByIndex(this, input, idx, parentStruct)
            % Find out which ports are "real" I/O ports (and not
            % 'function-call' ports) and return the 'idx'-th one
            %
            % This information is always available from
            % this.getInfoStruct.modelInterface, so look into that
            % structure here
            modelInterfacePorts = this.getPortsAsCellArray(input,...
                this.getInfoStruct.modelInterface);
            indexesOfIOPorts = find(cellfun(...
                @(port)(~port.IsFcnCall),... % Filter out 'function-call' ports
                modelInterfacePorts));
            
            % Get the collection of ports we need to search from the
            % structure with Inports and Outports elements that has been
            % passed in as a parameter
            allPorts = this.getPortsAsCellArray(input, parentStruct);
            
            ioPort = allPorts{indexesOfIOPorts(idx)};
        end
        
        function validateInstPCodeDescrInterface(this)
            % CodeDescriptor does not have the correct description for 
            % InstP mapped to Imported storage classes. 
            % We are also missing the initial value for the parameters,
            % see g2031457.
            
            instParameterArgs = this.getCodeInfoUtils.getInstanceSpecificParameters;
            for idx = 1:numel(instParameterArgs)
                currParam = instParameterArgs(idx);
                % A Parameter mapped to an imported storage class has a 
                % GraphicalName containing "InstParameterArgument" and its
                % implementation is a Variable with Identifier also 
                % containing "InstParameterArgument"
                if this.CodeInfoUtils.isa(currParam.Implementation, 'Variable') && ...
                        this.CodeInfoUtils.hasInstanceSpecificParameterName(currParam.Implementation.Identifier)
                    rtw.pil.ProductInfo.error('pil', 'UnsupportedImportedInstP', ...
                        this.getComponentForDiagnostics);
                end
            end
        end
    end
    
    methods (Access = ?rtw.pil.SILPILInterface)
        function updateSILPILInterface(this, interface)
            % update the cached SILPILInterface with and handle to a new
            % object
            this.SILPILInterface = interface;
        end
    end
end
