
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-2023 The MathWorks, Inc.

    % properties
    properties (SetAccess = 'private', GetAccess = 'protected')
        SILPILInterface;
        SigLogInfo;
        CommandData;
        MemUnitInfo;
        TimerType;
        ProfilingTaskRegistry; 
        ProfilingStackTaskRegistry;
        ProfilingOverheadRegistry = [];
        ProfilingCaptureRegistry = [];
        TypeUtils;
        ComponentArgs;
    end
    
    properties (SetAccess = 'protected', GetAccess = 'public')        
        CodeInstrumentationEnabled;
        ProfilingEnabled;
        StackProfilingEnabled;
        CodeStackProfileVariable;
        TimeProfilingEnabled;
        CodeProfilingSaveOptions;
        CodeExecutionProfileVariable;
        CodeProfilingTracing = false;
        % 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 mf.zero.Model {mustBeScalarOrEmpty} = ...
            mf.zero.Model.empty() % MF0 "root" model
        MF0ModelInfo {mustBeScalarOrEmpty} % Model interface data        
        % string array of workspace variable names
        SupportedTunableModelWorkspaceParamsArray = string.empty;
        RTESWCHeaderUtils; % utils for processing the AUTOSAR RTE header
    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 = 'protected')
        InstPArgumentIndices = []; % indices of supported InstP
    end
    
    properties (Access = 'private')
        ClientInterface;        
        ValidateCodeInfoUtils = true;
        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;
        DebuggingInfo;
    end

    properties (SetAccess = 'private', GetAccess = 'public')
        IsGRTBased = false;
    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

    properties (Access = 'protected', NonCopyable = true)
        % do not copy this property on copy operations
        DataStoresToTransferInterfaces; % DSM Data Interface cache
    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.getCodeName());
	    
            % 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);
            
            loopType = this.getSILPILInterface.getInTheLoopType();
            if loopType ~= rtw.pil.InTheLoopType.MATLABFcn
                % Load serialized MF0 from XML and cache in configInterface
                this.cacheMF0ModelInterfaceInfo(configInterface);
                
                % Check if top model XIL
                configInterface.setIsTopModelXILSim(this.getSILPILInterface.isTopModelXILSim);

                activeConfigSet = configInterface.getResolvedConfig;
                this.IsGRTBased = activeConfigSet.getComponent('Code Generation').getComponent('Target').isGRTTarget;                
            end
            
            % validate the configuration interface before initializing
            % CodeInfoUtils
            this.validateConfigInterface;
            
            % initialize CodeInfoUtils / CoderInfo
            this.resetCodeInfoUtils;                                                                     
                     
            % execution profiling
            lCodeExecutionProfilingEnabled = this.initExecutionProfilingSettings(loopType);
            
            % 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;
            this.initDebuggingInfo();
            
            % 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.RLSSubsystem}
            
                    this.TypeUtils = rtw.connectivity.SimulinkTypeUtils;

                otherwise
                    assert(false,'Invalid InTheLoopType');
            end

            if silPILInterface.getCodeInterfaceFactory.IsAutosar
                % RTE header utils
                this.RTESWCHeaderUtils = coder.internal.connectivity.RTESWCHeaderUtils(...
                    silPILInterface.getCodeDir(), ...
                    this.getCodeInfo().Name);
            end

            % initialize DataStoresToTransferInterfaces cache
            this.initDataStoresToTransferInterfaces();
        end                
        
        function isCoderAssumptionsEnabled = isCoderAssumptionsEnabled(this)
            % ComponentArgs hold the truth about whether the feature is
            % enabled
            isCoderAssumptionsEnabled = ...
                this.getComponentArgs().CoderAssumptionsEnabled;
        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 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       
        
        function res = isLoggingFromTransformedModel(~)
            res = false;
        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 setProfilingStackTaskRegistry(this, registry)            
            this.ProfilingStackTaskRegistry = registry;            
        end
        
        function registry = getProfilingStackTaskRegistry(this)            
            registry = this.ProfilingStackTaskRegistry;
        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 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, loopType)
            % Is execution profiling enabled
            this.TimeProfilingEnabled = strcmp...
                (this.getInstrumentationSettingsModelParam('CodeExecutionProfiling'), 'on');
            this.StackProfilingEnabled = strcmp...
                (this.getInstrumentationSettingsModelParam('CodeStackProfiling'), 'on');
            lProfilingEnabled = this.TimeProfilingEnabled || this.StackProfilingEnabled;
            this.ProfilingEnabled = lProfilingEnabled;

            if this.TimeProfilingEnabled
                this.CodeProfilingSaveOptions = this.getInstrumentationSettingsModelParam...
                    ('CodeProfilingSaveOptions');
                this.CodeExecutionProfileVariable = ...
                    this.getInstrumentationSettingsModelParam('CodeExecutionProfileVariable');
            end
            if this.StackProfilingEnabled
                this.CodeStackProfileVariable = this.getInstrumentationSettingsModelParam('CodeStackProfileVariable');
            end
            if loopType == rtw.pil.InTheLoopType.Block
                this.CodeProfilingTracing = false;
            else
                this.CodeProfilingTracing = strcmp('on', ...
                    this.getInstrumentationSettingsModelParam('CodeProfilingEnableTracing'));
            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 enabled = isTimeProfilingEnabled(this)
            enabled = this.TimeProfilingEnabled;
        end
        
        function enabled = isStackProfilingEnabled(this)
            enabled = this.StackProfilingEnabled;
        end

        function enabled = isVerboseOutputEnabled(this)
            enabled = this.ComponentArgs.getVerboseCommunicationOutput();
        end
        
        function lTimer = getProfilingTimer(this)
            % There must be a timer specification to support execution profiling
            lTimer = [];
            if this.isTimeProfilingEnabled
                targetServices = this.getTargetServices();
                lTimer = targetServices.getProfilingService().getTimer();
                if isempty(lTimer)
                    rtw.pil.ProductInfo.error('pil', 'ConnectivityConfigNoTimer', ...
                                              targetServices.getName());
                end
            end
        end
        
        function [lStack, isGeneric] = getProfilingStackDriver(this)
            % There must be a stack driver to support stack profiling
            lStack = [];
            isGeneric = false;
            if ~this.isStackProfilingEnabled
                return;
            end
            targetServices = this.getTargetServices();
            if targetServices.hasProfilingService() && ...
                    ~isempty(targetServices.getProfilingService().getStackDriver())
                lStack = targetServices.getProfilingService().getStackDriver();
                return;
            end
            lStack = coder.profile.generalStackDriver(this.getParam('ProdBitPerPointer'));
            isGeneric = true;
        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)
            supportedTunableWorkspaceParams = ...
                this.getCodeInfo.Parameters(this.SupportedTunableWorkspaceParams);
                index = find(arrayfun(@(x) rtw.connectivity.CodeInfoUtils.isEquivalentTo(dataInterface, x), ...
                    supportedTunableWorkspaceParams));
            assert(isempty(index) || isscalar(index), ...
                'Found multiple matching Parameters');
            isSupportedTunableWorkspaceParam = ~isempty(index);        
        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 = [];
            % build list of data types needed for state logging
            stateDataTypes = {};
            if this.getClientInterface.isStateLoggingEnabled(this)
                % find all states
                childLoggingParam = strcmp('on', ...
                    this.getInstrumentationSettingsModelParam('SILPILLoggingInReferencedModels'));
                dworks = this.getCodeInfoUtils.getLoggedStates(childLoggingParam);
                dworks = this.postProcessLoggedStates(dworks);
                % initialize states that can be logged
                inTheLoopType = this.getSILPILInterface.getInTheLoopType;
                [this.StateLoggingData, supportedDataInterfaces] = ...
                    this.getClientInterface.initializeStateLoggingData(this, ...
                    dworks, offset, inTheLoopType);
                
                %Extract all data types used for states, to ensure the
                %correct data type support is enabled
                for stateData = this.StateLoggingData
                    elements = stateData.getHostDataElements();
                    for elem = elements
                        stateDataTypes{end+1} = elem.SimulinkType; %#ok<AGROW>
                    end
                end
                stateDataTypes = unique(stateDataTypes);
            end
            
            this.CodeInfoUtils.setStateLogs(supportedDataInterfaces, this.StateLoggingData);
            this.CodeInfoUtils.updateTypeSupport(stateDataTypes);
        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 = {};
            if this.getClientInterface.isSignalLoggingEnabled(this)            
                % initialize for logging
                this.initSignalLogging;
                inTheLoopType = this.getSILPILInterface.getInTheLoopType;
                [loggingData, modelLoggingInfoStruct, supportedDataInterfaces, loggingIODataTypes] = ...
                    this.getClientInterface.initializeSignalLoggingData(this, ...
                        this.SigLogData, cachedXILBlockModelLoggingInfo, ...
                        inTheLoopType);
            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;
            
            this.CodeInfoUtils.updateTypeSupport(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);
            % now that validation has passed (part of constructor) set the
            % flag to false to avoid doing it again next time
            this.ValidateCodeInfoUtils = false;
            % 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), ...
                    this.getInstrumentationSettingsModel);
                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;
                    if isempty(rtw.pil.OwnerAccessor.getOwner(storageImplementation))
                        % prepend an extra tag to the identifier to further de-risk name
                        % clashes
                        tag = 'tgt_var_';
                        storageImplementation.Identifier = [tag storageImplementation.Identifier];
                    end
                case {'StructExpression', ...
                        'PointerExpression', ...
                        'ClassMemberExpression', ...
                        'StaticMemberExpression', ...
                        'ArrayExpression' }
                    % 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', ...
                        'AutosarSenderReceiver'}
                    storageImplementation = implementation;
                case {'Literal'}
                    storageImplementation = [];
                case {'TypedCollection'}
                    storageImplementation =this.getCodeInfoUtils.getArray(implementation.Elements);
                case {'BasicAccessFunctionExpressionCollection'}
                    storageImplementation =implementation.Elements(1);
                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)
            % Return cached interfaces
            dataStoresToTransfer = this.DataStoresToTransferInterfaces;
        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 = 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.getRootModel();
        end
        
        function isReadOnly = isComponentReadOnly(this)
            % by default, return readOnly if the component under test is a
            % protected model
            isReadOnly = this.isModelProtected;
        end
        
        function debuggingInfo = getDebuggingInfo(this)
            debuggingInfo = this.DebuggingInfo;
        end
        
        function isProtected = isModelProtected(this)
            isProtected = this.SILPILInterface.IsModelProtected;
        end
        
        function isSupported = isStateLoggingSupported(this)
            % logging is not supported for protected models or SIL/PIL
            % blocks
            isSupported = ~(this.SILPILInterface.IsModelProtected || ...
                (this.SILPILInterface.getInTheLoopType() == rtw.pil.InTheLoopType.Block));
        end
        
        function isSupported = isSignalLoggingSupported(this)
            % logging is not supported for protected models or SIL/PIL
            % blocks (unless under feature control) and SIL/PIL blocks
            isSupported = ~this.SILPILInterface.IsModelProtected && ...
                this.SILPILInterface.getInTheLoopType() ~= rtw.pil.InTheLoopType.Block;
        end
        
        function indices = getInstPArgumentIndices(this)
            indices = this.InstPArgumentIndices;
        end
        
        function needStorage = needStorageForAllocationErrorMessage(this)
            allocFunction = this.getAllocationFunctionForComponent();
            needStorage = ~isempty(allocFunction) && ...
                strcmp(this.getParam('SuppressErrorStatus'), 'off');
        end
        
        function includePrivateH = shouldIncludePrivateHeader(this) %#ok<MANU> 
            % Several variables defined in model.c may be declared in
            % model_private.h instead of model.h because not used outside.
            % However, the associated signals can be logged, so we need to
            % include model_private.h to have their declarations            
            includePrivateH = true;
        end
        
        function dependencies = getWorkflowSpecificDependencies(~)
            dependencies = {};
        end

        function needSupport = needServiceSupport(~)
            % default implementation
            needSupport = true;
        end

        % Does this flavor support tunable workspace parameters?
        function supports = supportsModelWorkspaceParams(this) %#ok
            supports = true;
        end

        function isRTMpointer = isRTMPointer(this, dataInterface)
            if ~this.CodeInfoUtils.isa(dataInterface.Implementation, 'PointerVariable')
                isRTMpointer = false;
                return;
            end
            RTMImplInTgtVar = strfind(dataInterface.Implementation.TargetVariable.Identifier,dataInterface.Implementation.Identifier);
            RTMInTgtVar = strfind(dataInterface.Implementation.TargetVariable.Identifier,dataInterface.GraphicalName);
            isRTMpointer = strcmp('RTModel', dataInterface.GraphicalName) && ...
                (~isempty(RTMImplInTgtVar) || ~isempty(RTMInTgtVar));
        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), ...
                        this.getInstrumentationSettingsModel);
                    if strcmp(covSettings.CoverageTool, SlCov.getCoverageToolName())
                        instrumentationSettingsModel = this.getInstrumentationSettingsModel;
                        modelName = this.SILPILInterface.getRootModel();
                        % @todo update the usage of edit-time filter filterOutCodeInactiveVariantSubsystemChoices()
                        % instead use the post-compile filter codeCompileVariants() - g2603391
                        refModelsList = find_mdlrefs(modelName ,'MatchFilter',@Simulink.match.internal.filterOutCodeInactiveVariantSubsystemChoices); % look only inside code active choice of VSS
                        for ii = 1:numel(refModelsList)
                            if SlCov.CodeCovUtils.isXILCoverageEnabled(instrumentationSettingsModel, refModelsList{ii}, this.SILPILInterface.isSIL())
                                enabled = true;
                                return
                            end
                        end
                    end
                end
            end
        end
        
        function [isTunable, e] = checkParameterTunabilitySupport(this, param)
            % assume parameter is not tunable and do checks
            isTunable = false;
            e = MException.empty();
            
            % 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);
                return;
            end
            
            % Skip SystemConstants (empty type implies SystemConstant)
            if this.CodeInfoUtils.isDataInterfaceSystemConstant(param)
                return;
            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);
                end
                return;
            end
            
            %Skip parameters in inactive variants. SIL/PIL only generates
            %code for the active variant
            isActiveVariant = isempty(param.VariantInfo) || ...
                this.CodeInfoUtils.isActiveVariant(param.VariantInfo);
            if ~isActiveVariant
                return;
            end

            
            % always warn for read only 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);
                return;
            end
            
            % always warn for static parameters
            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);
                return;
            end
            
            % parameter can be tuned
            isTunable = true;
        end
        
        function sigLogInfo = processUnloggedSignals(~, sigLogInfo)
            % override this method to update the list of unlogged signals
        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               

        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

        function validateUnsupportedDataStoreReferences(~)
            % default implementation, overridden by subclasses if needed
        end
        
        function isTunable = checkParameterTunabilitySupportAndAppendWarning(this, param, paramIdx)
            
            [isTunable, exception] = this.checkParameterTunabilitySupport(param);
                           
            if ~isempty(exception)
                this.UnsupportedTunableParameterWarningInfo(end+1).ParamIdx = paramIdx;
                this.UnsupportedTunableParameterWarningInfo(end).Exception = exception;
            end
        end
        
        function newXilAppDir = appendXILApplicationDirChild(~, xilAppDir)
            % return the xilAppDir by default
            newXilAppDir = xilAppDir;
        end
        
        function allocFunction = getAllocationFunctionForComponent(this)
            allocFunction = this.getCodeInfo().AllocationFunction;
        end

        function logData = postProcessLoggedSignals(~, logData)
            % this method post-process the DataInterfaces read from Code 
            % Descriptor about the signals to log. Child classes can
            % override this method to apply their transformation
        end
    
        function dworks = postProcessLoggedStates(~, dworks)
            % this method post-process the DataInterfaces read from Code 
            % Descriptor about the states to log. Child classes can
            % override this method to apply their transformation
        end

        function cacheMF0ModelInterfaceInfoHook(~, ~, ~)
            % Default implementation. Child classes can override this
            % method to apply their transformation
        end

        function initDataStoresToTransferInterfaces(this)
            % This method initializes DataStoresToTransferInterfaces cache.
            % It is necessary to cache DSM data interfaces to avoid looping
            % over the interfaces every time we call 
            % "getDataStoresToTransferInterfaces". This can cause a
            % significant performance regression for models with large
            % quantities of DSM's. This is a Default implementation that 
            % child classes can override.
            allInterfaces = this.CodeInfoUtils.getAllSharedDataStores();
            idxIsGlobalDSM = false(1, numel(allInterfaces));
            for idx=1:numel(allInterfaces)
                %Include only Global DSMs
                idxIsGlobalDSM(idx) = isempty(allInterfaces(idx).SharedLocalDSMInfo);
            end
            this.DataStoresToTransferInterfaces = allInterfaces(idxIsGlobalDSM);
        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
            
            % 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
                    covSettings = slprivate('getCodeCoverageSettings', ...
                        @(name) this.getInstrumentationSettingsModelParam(name), ...
                        this.getInstrumentationSettingsModel);
                    if strcmp(covSettings.CoverageTool, SlCov.getCoverageToolName())
                        % Simulink Coverage case
                        rtw.pil.ProductInfo.error('pil', 'pilGRTCodeCoverage', ...
                            this.getComponentForDiagnostics);
                    elseif ~strcmp(covSettings.CoverageTool, 'None')
                        % third-party code coverage case
                        rtw.pil.ProductInfo.error('pil', 'pilGRTCovSettings', ...
                            this.getComponentForDiagnostics);
                    end
                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 && ~inport.IsDynamicArray
                            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 && ~outport.IsDynamicArray
                            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')
                componentName = this.getComponentForDiagnostics();
                if this.getCodeInfoUtils.isMultiRate
                    isAUTOSARAdaptive = rtw.pil.ModelBlockPIL.isAUTOSARAdaptive(this.getConfigInterface);                    
                    % AUTOSAR Adaptive is allowed under feature control
                    if ~(isAUTOSARAdaptive && matlab.internal.feature('SILMdlRefAUTOSARAdaptiveConcurrentExecution'))                        
                        rtw.pil.ProductInfo.error('pil', 'ConcurrentTasks', ...
                            componentName);
                    else
                        % For non-deterministic H2L priority RTB's there is specialized handling of the initial
                        % condition (IC) in Normal Mode in order to guarantee it is realized at the first time
                        % step. Due to the nature of concurrency, on a real time target there is no guarantee of
                        % the execution order, which can spawn different behaviors. This specialization of the
                        % IC can cause numerical mismatches in SIL, which runs on a single thread with no
                        % specialized handling of IC's. Therefore, we will warn for possible numerical mismatches.
                        [isWarningRequired, requiresNonHiddenRtbFixit, requiresHiddenRtbFixit] = ...
                            this.isRtbMismatchWarnRequired();
                        if isWarningRequired
                            diagObj = MSLDiagnostic(message("PIL:pil:RTBThatCanCauseNumericalMismatch", componentName));
                            % Only add fixit if the model is not read only
                            if ~this.isComponentReadOnly()
                                if requiresNonHiddenRtbFixit
                                    rtbBlockPathList = this.getRtbMismatchWarnBlockPathList();
                                    cause = MSLDiagnostic(message('PIL:pil:RTBThatCanCauseNumericalMismatch_explicitCause', ...
                                        componentName, rtbBlockPathList));
                                    diagObj = diagObj.addCause(cause);
                                    fixitTag = 'enableDeterministicRTB';
                                    fixitMsg = message('PIL:pil:RTBEnableDeterministic_fixitmsg');
                                    callback = sprintf( ...
                                        'rtw.pil.SILPILFixits.enableDeterministicRTB(''%s'')', rtbBlockPathList);
                                    diagObj.addFixit(fixitMsg.getString(), callback, fixitTag);
                                end
                                if requiresHiddenRtbFixit
                                    cause = MSLDiagnostic(message('PIL:pil:RTBThatCanCauseNumericalMismatch_implicitCause', ...
                                        componentName));
                                    diagObj = diagObj.addCause(cause);
                                    fixitTag = 'enableAutoInsertRateTranBlk';
                                    fixitMsg = message('PIL:pil:HiddenRTBInsertRTBMode_fixitmsg');
                                    callback = sprintf('rtw.pil.SILPILFixits.fix(''InsertRTBMode'',''%s'')', componentName);
                                    diagObj.addFixit(fixitMsg.getString(), callback, fixitTag);
                                end
                            end
                            diagObj.reportAsWarning();
                        end
                    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 = this.SILPILInterface.isTopModelSILPILBlock() && ...
                this.CodeInfoUtils.hasExpInportsSpecification;
            
            % 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".   
                component = this.getComponentForDiagnostics();
                DAStudio.error('PIL:pil:TopModelTaskSpecNotSupported', ...
                    component, component);
            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
                    component = this.getComponentForDiagnostics();
                    DAStudio.error('PIL:pil:SILPILBlockDSMWriteInInit', component);
                end
                
            end
            if inTheLoopType==rtw.pil.InTheLoopType.Block
                if ~isempty(this.CodeInfoUtils.getSharedLocalDataStores)
                    component = this.getComponentForDiagnostics();
                    DAStudio.error('PIL:pil:PILBlockSharedLocalDSMS', component);
                end
            end
            % This check requires access to the wrapper model used for SIL/PIL simulation
            % but is not available during the SLDV analysis (however can safely skip the check
            % since it has already been done when running XIL which is a prerequisite for the SLDV analysis)
            if this.getSILPILInterface.isModelBlockXIL && ~isTopModelXILSim && ~this.getSILPILInterface.IsForSldvAnalysis
                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;
            this.validateUnsupportedDataStoreReferences();
        end

        function componentArgs = createComponentArgs(this, ...
                lCodeExecutionProfilingEnabled, lXilCompInfo)
            % see isUpToDate to see when ComponentArgs & Connectivity
            % Config is refreshed.
            
            % get the code generation directory
            pilInterface = this.getSILPILInterface();
            componentCodePath = pilInterface.getCodeDirForComponentArgs();

            % get the code generation component name
            componentCodeName = pilInterface.getCodeName();
            
            % get the fullpath to the sil/pil application dir
            applicationCodePath = this.getXILApplicationDir();

            % make sure tgt dir exists
            if ~exist(applicationCodePath, 'dir')
                mkdir(applicationCodePath);
            end
            
            % create the componentArgs object
            componentArgs = this.getClientInterface.createComponentArgs(pilInterface.getRootModel(), ...
                                                              componentCodePath, ...
                                                              componentCodeName, ...
                                                              applicationCodePath, ...
                                                              pilInterface, ...
                                                              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(pilInterface.CodeDescriptorDir);
            componentArgs.setComponentForDiagnostics(this.getComponentForDiagnostics());
            
            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);  
                isTunable = this.checkParameterTunabilitySupportAndAppendWarning(param, paramIdx);      
                if isTunable
                    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];                            
                            this.SupportedTunableModelWorkspaceParamsArray(end+1) = 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
        end                                                          
        
        function initSignalLogging(this)
            childLoggingParam = strcmp('on', ...
                this.getInstrumentationSettingsModelParam('SILPILLoggingInReferencedModels'));
            logData = ...
                    this.getCodeInfoUtils.getLoggedSignals(...
                        this.getSILPILInterface.getInTheLoopType, childLoggingParam);
            this.SigLogData = this.postProcessLoggedSignals(logData);
        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;
           extendTimeout = clientInterface.extendTimeOutForSIL(isSIL, this.getConfigInterface);
           
           rtIOStream.addPreOpenHook(@() clientInterface.rtIOStreamPreOpen(rtIOStream, extendTimeout));
       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
        
        function xilAppDir = getXILApplicationDir(this)
            % determine the sil/pil sub dir
            pilInterface = this.getSILPILInterface();
            if pilInterface.isSIL()
                xilSubDir = rtw.connectivity.Utils.getSilSubDir;
            else
                xilSubDir = rtw.connectivity.Utils.getPilSubDir;
            end
            xilAppDir = fullfile(pilInterface.CodeDescriptorDir, xilSubDir);
            xilAppDir = this.appendXILApplicationDirChild(xilAppDir);
        end
        
        function initDebuggingInfo(this)
            
            fullClassName = '';
            if this.getSILPILInterface.getInTheLoopType() == rtw.pil.InTheLoopType.MATLABFcn
                
                nameSpace = this.getParam('CppNamespace');
                className =  this.getParam('CppInterfaceClassName');
                if ~isempty(nameSpace)
                    if ~isempty(className)
                        fullClassName = sprintf('%s::%s', nameSpace, className);
                    else
                        fullClassName = nameSpace;
                    end
                else
                    fullClassName = className;
                end
            end
            
            debugSettings = this.getDebugSettings();
            if debugSettings.IsDebuggingEnabled && ...
                    debugSettings.IsSimulationStepperEnabled && ~ispc
                % this is the common error currently in place for DDD and
                % LLDB, we should review this an make it debugger specific
                exception = MSLException(message('PIL:pil:LinuxSILDebugStepperNotSupported'));
                exception.throw;
            end
            % store setting of Debugginginfo
            this.DebuggingInfo = rtw.connectivity.SILDebuggingInfo(...
                debugSettings.IsDebuggingEnabled, ...
                this.isCPPEncap, ...
                this.getCodeInfo, ...
                fullClassName, ... 
                [], ...
                debugSettings.IsSimulationStepperEnabled, ...
                this.CodeProfilingTracing);
        end
        
        function debugSettings = getDebugSettings(this)
            % return the current configuration for SIL debugging
            
            debugSettings.IsDebuggingEnabled = ...
                strcmp(this.getInstrumentationSettingsModelParam('SILDebugging'), 'on');
            
            % Cannot use getInstrumentationSettingsModelParam because
            % EnablePauseTimes is not a configset parameter, instead get 
            % the top context that controls the simulation
            switch this.SILPILInterface.getInTheLoopType
                case 'Block'
                    topContext = this.SILPILInterface.getBlockPath();
                    if isempty(topContext)
                        % This is empty when building a pilblock, default to
                        % false as this value does not matter at build time.
                        % The correct value will be populated at runtime when 
                        % building the application. 
                        debugSettings.IsSimulationStepperEnabled = false;
                    else
                        debugSettings.IsSimulationStepperEnabled = ...
                            strcmp(get_param(bdroot(topContext{1}), 'EnablePauseTimes'), 'on');
                    end
                case 'MATLABFcn'
                    debugSettings.IsSimulationStepperEnabled = false;
                otherwise
                    topContext = this.getInstrumentationSettingsModel();
                    debugSettings.IsSimulationStepperEnabled = ...
                        strcmp(get_param(topContext, 'EnablePauseTimes'), 'on');
            end
            
        end

        function cacheMF0ModelInterfaceInfo(this, configInterface)
            % Load serialized MF0 from XML and cache in configInterface
            [this.MF0ModelInfo, lParser] = this.getSILPILInterface().getMF0ModelInfo();
            this.MF0Model = lParser.Model;  % Need to persist model
            configInterface.setMF0ModelInfo(this.MF0ModelInfo, this.MF0Model);
            % invoke customization hook
            this.cacheMF0ModelInterfaceInfoHook(configInterface, lParser);
        end

        function [isWarningRequired, requiresNonHiddenRtbFixit, requiresHiddenRtbFixit] = ...
                isRtbMismatchWarnRequired(this)
            % Check if a warning is required for potential numerical
            % mismatches due to RTB blocks in concurrent execution. Further
            % check what fix-its we are able to add to the warning.
            periodicEventPortUnsupportedBlockInfo = this.MF0ModelInfo.periodicEventPortUnsupportedBlockInfo;
            requiresNonHiddenRtbFixit = ...
                ~isempty(periodicEventPortUnsupportedBlockInfo.nonDeterministicRTBsWithSpecializedHandlingOfIC.toArray);
            requiresHiddenRtbFixit = ...
                periodicEventPortUnsupportedBlockInfo.hasHiddenNonDeterministicRTBsWithSpecializedHandlingOfIC;
            isWarningRequired = requiresNonHiddenRtbFixit || requiresHiddenRtbFixit;
        end

        function rtbBlockPathList = getRtbMismatchWarnBlockPathList(this)
            % Create RTB block path list string delimited by commas
            periodicEventPortUnsupportedBlockInfo = this.MF0ModelInfo.periodicEventPortUnsupportedBlockInfo;
            rtbBlocks = ...
                periodicEventPortUnsupportedBlockInfo.nonDeterministicRTBsWithSpecializedHandlingOfIC.toArray();
            assert(~isempty(rtbBlocks));
            rtbBlockPathList = strjoin(string(rtbBlocks), ', ');
        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
