classdef SILPILInterface < matlab.mixin.Copyable
    %SILPILINTERFACE provides shared infrastructure for the SIL/PIL clients
    % (SIL/PIL block, Top-Model SIL/PIL, Model block SIL/PIL) and provides
    % support for the Target Connectivity API.
    %
    %   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 (Constant=true, GetAccess=public)
        % Execution profiling name of function to collect data
        TargetCodeInstrFcnName = 'xilUploadCodeInstrData';
    end                           
    
    properties (SetAccess = 'private', GetAccess = 'private')        
        % Root directory for the SIL/PIL build
        AnchorDir;
        % Code generation directory and name
        CodeDir; % It indicates either a code generation directory or a 
                 % 'resources' folder depending on the value of the 
                 % UsingResourcesDir property
        CodeName;
        % Code Interface Abstraction Utilities
        CodeInterfaceFactory;
        SILPILWrapperUtils;
        SlprjInternalPath;        
        InTheLoopType;
        TopModelForModelBlockPIL;
        % Connectivity Config Info
        TargetServices coder.connectivity.targetservices.ITargetServices = coder.connectivity.connectivityconfig.TargetServices.empty();
        % store an owner for this object
        Owner;        
        % props associated with slbuild_profile
        IsProfilingEnabled;
        ProfilingMdlRefTgtType;  
        InstrumentationInfoAddedToBuildInfo=false;
        TimerInfoAddedToBuildInfo=false;
        % Target wrapper info
        WrapperSourceInfo;    
        WrapperInfo; % cache of wrapperInfo.mat
        ClientInterface;
        InfoStruct;
        XRelSourceFile; % Full path to a file in the exported code location
                        % that contains one of the generated entry point
                        % functions
        XRelIsTopModelCode; % Logical, true if the imported code has a 
                            % TopModel code interface
        XRelIsReusable;     % Logical, true if the imported code has a
                            % multi-instance code interface
        UsingResourcesDir; % If false, the CodeDir property is set to a regular
                           % build folder. Cross-release blocks always
                           % use resources dir, but during their build
                           % process when the property is set to false and
                           % CodeDir point to a regular build folder.
        BlockPath;
        
        SfunctionBinDir;    % Location that S-function binary is put
        ParallelBuildCodeDir;   % Used when generating S-function in different location to code (parallel build, code not updated, S-function needs updating)
    end        
    
    properties (SetAccess = 'private', GetAccess = 'public')
        XRelDWorkTypes = {}; % Cell array containing types of DWorks for cross release
                             % reusable model ref code interface
        XRelDWorkNames = {}; % Cell array containing names of DWorks for cross release
                             % reusable model ref code interface
        XRelOrderedParamNames = {}; % Cell array containing (ordered) list of model parameter names for cross-release model
        IsUpdateModelReferenceTargets;
        SuppressIsSILAndPWS;
        CodeDescriptorDir; % Location of codedescriptor.dmr file
        ApplicationExecutable;
        RLSSubsystemName;
        IsModelProtected logical = false;
        ProtectedModelFullName char = '';
        % True if the SILPILInterface is used
        % for generating an interface compatible with SLDV
        IsForSldvAnalysis logical = false
        IsSIL; % Should move to private get access after g2205430 resolved.
    end

    methods                        
        function this = SILPILInterface(anchorDir, ...
                                        codeDir, ...
                                        inTheLoopType, ...
                                        clientInterface, ...
                                        targetServices, ... 
                                        lXilCompInfo, ...
                                        isSIL, ...
                                        varargin)
            %
            % Constructor
            %
            
            p = inputParser;
            p.addParameter('TopModel', '', ...
                @(x)validateattributes(x, {'char'}, ...
                {'nonempty'}, mfilename, 'TopModel'));
            p.addParameter('XRelSourceFile', '', ...
                @(x)validateattributes(x, {'char'}, {}, ...
                mfilename, 'XRelSourceFile'));
            p.addParameter('XRelIsTopModelCode', true, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'XRelIsTopModelCode'));
            p.addParameter('XRelIsReusable', false, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'XRelIsReusable'));
            p.addParameter('XRelDWorkTypes', {}, ...
                @(x)validateattributes(x, {'cell'}, {}, ...
                mfilename, 'XRelDWorkTypes'));
            p.addParameter('XRelDWorkNames', {}, ...
                @(x)validateattributes(x, {'cell'}, {}, ...
                mfilename, 'XRelDWorkNames'));
            p.addParameter('XRelOrderedParamNames', {}, ...
                @(x)validateattributes(x, {'cell'}, {}, ...
                mfilename, 'XRelOrderedParamNames'));
            p.addParameter('UsingResourcesDir', false, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'UsingResourcesDir'));
            p.addParameter('TopModelOriginalParams', []);
            p.addParameter('ForSldv', false, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'ForSldv'));
            p.addParameter('IsUpdateModelReferenceTargets', false, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'IsUpdateModelReferenceTargets'));
            p.addParameter('SuppressIsSILAndPWS', false, ...
                @(x)validateattributes(x, {'logical'}, {}, ...
                mfilename, 'SuppressIsSILAndPWS'));
            % By default the SFunctionBinDir should be the anchor dir.
            % Except when it is set explicitly for parallel build.
            p.addParameter('SfunctionBinDir', anchorDir, ...
                @(x)validateattributes(x, {'char'}, {}, ...
                mfilename, 'SfunctionBinDir'));
            p.addParameter('ParallelBuildCodeDir', '', ...
                @(x)validateattributes(x, {'char'}, {}, ...
                mfilename, 'ParallelBuildCodeDir'));
            p.addParameter('Owner', '', ...
                @(x)validateattributes(x, {'char','double'}, {}, ...
                mfilename, 'Owner'));
            p.addParameter('BlockPath', {}, ...
                @(x)validateattributes(x, {'cell'}, {}, ...
                mfilename, 'BlockPath'));
            p.addParameter('CodeDescriptorDir', codeDir, ... % By default match codeDir
                @(x)validateattributes(x, {'char'}, {}, ...
                mfilename, 'CodeDescriptorDir'));
            p.addParameter('RLSSubsystemName', '', ...
                @(x)validateattributes(x, {'char'}, {}, ...
                mfilename, 'RLSSubsystemName'));
            
            
            p.parse(varargin{:});
            
            topModel = p.Results.TopModel;
            topModelOriginalParams = p.Results.TopModelOriginalParams;
            this.UsingResourcesDir = p.Results.UsingResourcesDir;
            
            this.IsForSldvAnalysis = p.Results.ForSldv;
            this.IsUpdateModelReferenceTargets = p.Results.IsUpdateModelReferenceTargets;
            this.SuppressIsSILAndPWS = p.Results.SuppressIsSILAndPWS;
            
            % process codeDir
            this.validateStringArg(codeDir);
            this.CodeDir = codeDir;                        
                       
            this.SfunctionBinDir = p.Results.SfunctionBinDir;
            this.ParallelBuildCodeDir = p.Results.ParallelBuildCodeDir;
            this.Owner = p.Results.Owner;
            assert(iscellstr(p.Results.BlockPath), 'Block path should be a cell array of strings');
            this.BlockPath = p.Results.BlockPath;
            this.CodeDescriptorDir = p.Results.CodeDescriptorDir;
            codeDirForInfoStructInit = this.CodeDescriptorDir;
            this.RLSSubsystemName = p.Results.RLSSubsystemName;
            
            rtw.connectivity.Utils.validateArg(clientInterface, ...
                                               'coder.connectivity.ClientInterface');   
            this.ClientInterface = clientInterface;            
                        
            rtw.connectivity.Utils.validateArg(inTheLoopType, ...
                                               'rtw.pil.InTheLoopType');             
            this.InTheLoopType = inTheLoopType;     
            
            this.IsSIL = isSIL;
            
            switch inTheLoopType
                case {rtw.pil.InTheLoopType.ModelBlock, ...
                      rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                      rtw.pil.InTheLoopType.Subsystem}
                    this.setTopModelForModelBlockPIL(topModel);                                                
                case rtw.pil.InTheLoopType.MATLABFcn
                    %Need to encode function name back into code for infoStruct
                    %initialization below.
                    codeDirForInfoStructInit = fullfile(this.CodeDescriptorDir, topModel);
                case rtw.pil.InTheLoopType.Block
                    this.XRelSourceFile = p.Results.XRelSourceFile;
                    this.XRelIsTopModelCode = p.Results.XRelIsTopModelCode;
                    this.XRelIsReusable = p.Results.XRelIsReusable;
                    this.XRelDWorkTypes = p.Results.XRelDWorkTypes;
                    this.XRelDWorkNames = p.Results.XRelDWorkNames;
                    this.XRelOrderedParamNames = p.Results.XRelOrderedParamNames;
            end            
            
            this.TargetServices = targetServices;
            
            % load binfo
            if this.isXRelBlock && this.UsingResourcesDir
                % load binfo from the relocatable _resources folder
                usingResourcesDir = true;
                [this.SlprjInternalPath, ...
                    this.CodeName, ...
                    this.InfoStruct] = this.ClientInterface.initializeInfoStruct(codeDirForInfoStructInit, inTheLoopType, usingResourcesDir);
            else
                try
                    
                    [this.SlprjInternalPath, ...
                        this.CodeName, ...
                        this.InfoStruct] = this.ClientInterface.initializeInfoStruct(codeDirForInfoStructInit, inTheLoopType);
                catch origE
                    % construct standard error with origE as cause
                    [newId, newMessage] = rtw.pil.ProductInfo.message('pilverification', 'ErrorGeneratedCodeIncomplete');
                    newException = MException(newId, ...
                        newMessage);
                    newException = newException.addCause(origE);
                    throw(newException);
                end
            end
            
            % process anchorDir
            this.validateStringArg(anchorDir);
            this.AnchorDir = anchorDir;
            
            % check CodeName was initialized
            this.validateStringArg(this.CodeName);
            % check that we can determine the componentPath now
            assert(~isempty(this.getFullComponentPath), 'componentPath could no be determined');
            
            if (this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlock) || ...
                    (this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone)
                [this.IsModelProtected, this.ProtectedModelFullName] = ...
                    slInternal('getReferencedModelFileInformation', this.getFullComponentPath);
            end
            % setup for slbuild_profile            
            [this.IsProfilingEnabled, ...
             this.ProfilingMdlRefTgtType] = this.ClientInterface.initializePerfProfiling(...
             this.getRootModel, inTheLoopType);
                        
            % initialize the code interface factory
            % create ConfigInterface because WrapperUtils has not been
            % created yet...
            config = this.ClientInterface.createConfigInterface(this.InfoStruct.configSet, this.getCodeGenModel);            
            this.CodeInterfaceFactory = this.ClientInterface.createCodeInterfaceFactory(this, ...
                                                                                        config);
            
            % use CodeInterfaceFactory to initialize the SIL/PIL 
            % Wrapper Utilities
            this.SILPILWrapperUtils = this.CodeInterfaceFactory.createSILPILWrapperUtils(...
                this, ...
                this.getInfoStruct, ...
                this.ClientInterface, ...
                topModelOriginalParams, ...
                lXilCompInfo);
                        
            % initialize Target Wrapper Info
            this.initWrapperSourceInfo;                         
        end
        
        % accessor for SIL PIL Wrapper Utilities
        function SILPILWrapperUtils = getSILPILWrapperUtils(this)
            SILPILWrapperUtils = this.SILPILWrapperUtils;
        end               
        
        function infoStruct = getInfoStruct(this)            
            infoStruct = this.InfoStruct;
        end
        
        function targetServices = getTargetServices(this)
            targetServices = this.TargetServices;
        end
                
        function inTheLoopType = getInTheLoopType(this)
           inTheLoopType = this.InTheLoopType; 
        end      
        
        function isModelBlockXIL = isModelBlockXIL(this)
            isModelBlockXIL = ((this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlock) || ...
                               (this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone));            
        end
        
        function isTopModelXILSim = isTopModelXILSim(this)
            isTopModelXILSim = (this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone) && ...
                coder.connectivity.TopModelSILPIL.isTopModelXILHarness(this.getTopModelForModelBlockPIL);
        end
        
        function workspaceParams = getTopModelXILSFunctionParameters(this)           
            workspaceParams = ...
                this.getSILPILWrapperUtils.SupportedTunableModelWorkspaceParamsString;            
        end
        
        function isXRel = isXRelBlock(this)
            
            isXRel = ~isempty(this.XRelSourceFile);
        end
        
        function isFromTopModelCode = isXRelBlockFromTopModelCode(this)
            isFromTopModelCode = ~isempty(this.XRelSourceFile) && this.XRelIsTopModelCode;
        end
        
        function ret = isXRelBlockReusable(this)
            ret = ~isempty(this.XRelSourceFile) && this.XRelIsReusable;
        end
        
        
        function setTopModelForModelBlockPIL(this, model)
            this.TopModelForModelBlockPIL = model;
        end
        
        function model = getTopModelForModelBlockPIL(this)
            model = this.TopModelForModelBlockPIL;
        end
        
        function isSIL = isSIL(this)
            isSIL = this.IsSIL;
        end
        
        function simModeAcronym = getSimModeAcronym(this)
            if this.isSIL               
                simModeAcronym = 'SIL';
            else
                simModeAcronym = 'PIL';
            end 
        end
        
        function componentPath = getFullComponentPath(this)
            % derive full Simulink component path          
            %
            % only rely on SourceSubsystemName for SIL/PIL block
            if (this.InTheLoopType == rtw.pil.InTheLoopType.Block) && ...                                            
               ~isempty(this.getInfoStruct.SourceSubsystemName) 
                % componentPath is stored in binfo.mat
                componentPath = this.getInfoStruct.SourceSubsystemName;                            
            else
                % componentPath is componentName
                componentPath = this.getCodeName; 
            end                                    
            % handle new lines, which are equivalent to spaces
            % no need to escape /'s; this is already done in ss2mdl / binfo
            componentPath = strrep(componentPath, newline, ' ');
        end
        
        function rootModel = getRootModel(this)
            % rootModel of Simulink component path
            rootModel = this.getRootModelAndSystemPath;
        end
        
        function [rootModel, sysPath] = getRootModelAndSystemPath(this)
            % split Simulink component path to return the rootModel
            % and the rest of the component path
            [rootModel, sysPath] = strtok(this.getFullComponentPath, '/');
        end                
        
        function isSubsystemComponent = isSubsystemComponent(this)            
            [~, sysPath] = this.getRootModelAndSystemPath;                                    
            isSubsystemComponent = ~isempty(sysPath); 
        end
        
        function codeGenModel = getCodeGenModel(this)
            if this.InTheLoopType == rtw.pil.InTheLoopType.Subsystem
                codeGenModel = rtw.pil.RLSUtils.getUniqueContextName(this.getInfoStruct.modelName);
            else
                codeGenModel = this.getInfoStruct.modelName; 
            end
        end
                
        function codeDir = getCodeDir(this)
            % return either the code generation directory or the 
            % 'resources' folder 
            codeDir = this.CodeDir;                             
        end
        
        function codeName = getCodeName(this)
            % return the code generation code gen name
            codeName = this.CodeName;
        end
        
        function [codeDir, codeName, xilAppDir] = getCodeDirsForComponentArgs(this)
            % return the code generation directory, the component name and
            % the xil application directory
            hasParallelBuildCodeDir = ~isempty(this.ParallelBuildCodeDir);
            if hasParallelBuildCodeDir
                codeDir = this.ParallelBuildCodeDir;
            else
                codeDir = this.CodeDir;
            end
            if this.InTheLoopType == rtw.pil.InTheLoopType.Subsystem
                codeName = rtw.pil.RLSUtils.getUniqueContextName(this.CodeName);
            else
                codeName = this.CodeName;
            end
            
            % determine the sil/pil sub dir
            if this.isSIL
                xilSubDir = rtw.connectivity.Utils.getSilSubDir;
            else
                xilSubDir = rtw.connectivity.Utils.getPilSubDir;
            end
            xilAppDir = fullfile(this.CodeDescriptorDir, xilSubDir);
        end
        
        function applicationDir = getAnchorFolder(this)
            % return the anchor folder for the overall build
            applicationDir = this.AnchorDir;
        end       
        
        function sFunctionBinDir = getSfunctionBinDir(this)
            sFunctionBinDir = this.SfunctionBinDir;
        end
        
        function applicationDir = getApplicationDir(this)
            % return the SIL/PIL application dir            
            componentArgs = this.getSILPILWrapperUtils.getComponentArgs();
            applicationDir = componentArgs.getApplicationCodePath;
        end                
                        
        function [sfunName, sfunSourcePath, sfunBinDir] = getPILSfunction(this)            
            % sfunName - name of the host S-function
            % sfunSourcePath - full source path of the host S-function
            % sfunBinDir - binary directory
            
            % get the Application Target Src Dir
            appDir = this.getApplicationDir;
            % construct the name
            componentName = this.getSILPILWrapperUtils.getComponentForSfuncName;
            if this.isSIL
                suffixStart = '_s';
            else
                suffixStart = '_p';
            end
            switch this.getInTheLoopType
                case rtw.pil.InTheLoopType.ModelBlock
                    % Model block SIL s-function                        
                    suffixEnd = 'sf';                        
                case {rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                        rtw.pil.InTheLoopType.Subsystem}
                    % use standalone suffix for subsystem because the
                    % synthetic block has 'Top model' CodeInterface
                    suffixEnd = 'bs'; 
                case rtw.pil.InTheLoopType.MATLABFcn
                    suffixEnd = 'il';
                case rtw.pil.InTheLoopType.Block
                    % SIL block/Top Model s-function
                    if this.isXRelBlock
                        suffixEnd = 'il';
                    else
                        suffixEnd = 'bs';
                    end
                otherwise
                    assert(false, 'InTheLoopType not Supported');
            end            
            suffix = [suffixStart suffixEnd];
            
            % check componentName results in a valid s-function
            maxComponentNameLength = namelengthmax - length(suffix);
            componentNameLength = length(componentName);
            componentType = this.getSimModeAcronym;            
            if componentNameLength > maxComponentNameLength
                rtw.pil.ProductInfo.error('pilverification', ...
                    'SfunctionNameTooLong', ...
                    componentType, ...
                    componentName, ...
                    maxComponentNameLength);
            end
            sfunName = [componentName suffix];
            sfunSourcePath = fullfile(appDir, [sfunName '.c']);
            sfunBinDir = this.getSfunctionBinDir();
        end
        
        function [wrapperName, wrapperPath] = getWrapperSourceInfo(this)
            % wrapperName: name of target wrapper file
            % wrapperPath: path of target wrapper file
            assert(~isempty(this.WrapperSourceInfo), 'WrapperSourceInfo is not initialized.');
            wrapperName = this.WrapperSourceInfo.Name;
            wrapperPath = this.WrapperSourceInfo.Path;
        end                

        function [file, path] = getAccessFcnsSourceInfo(this)
            targetLang = this.SILPILWrapperUtils.getParam('TargetLang');
            filename = 'xil_access_fcns';
            switch targetLang
              case 'C'
                extension = '.c';
              case 'C++'
                extension = '.cpp';
              otherwise
                assert(false, 'Unknown TargetLang: %s', targetLang);
            end
            
            file = [filename extension];
            path = this.getApplicationDir;
        end
        
        
        function isUpToDate = isUpToDate(this, ...
                                         codeDir, ...
                                         targetService, topModel)
            % SILPILInterface and Config are up to date if:
            %
            % 1) codeDir, config have not changed            
            % 2) xil_interface.c/cpp is more recent than codeInfo.mat
            %
            % Note on #1: codeName is always contained within codeDir, so
            % we don't explicitly check it.
            %
            % Note on #2: Connectivity Configuration implementations 
            % rely on the binfo config set created during code generation;
            % it is part of the internal data of ComponentArgs.
            % 
            % We refresh whenever we detect that new code for the component
            % has been generated.   This fully re-initializes this object
            % (e.g. CodeInfo, binfo, ComponentArgs, Connectivity Config,
            % etc.)
            %                        
            % ENHANCEMENT: instead of the caller recreating the instance of
            % SILPILInterface this class could auto-reinitialize all items 
            % in the constructor when it detects it is required.
            %
            
            if nargin < 4
                % This argument is only supplied for Model block case
                topModel = '';
            end
            
            this.validateStringArg(codeDir);
            rtw.connectivity.Utils.validateArg(targetService, ...
                                               'coder.connectivity.targetservices.ITargetServices');                         
            % true, initially
            isUpToDate = true;
            % check codeDir
            if ~strcmp(this.getCodeDir, codeDir)
               isUpToDate = false;           
            end            
            if isUpToDate
                isUpToDate = this.isMatchingConfig(targetService.getServiceInstanceIdentifier());
            end            
            %
            if isUpToDate
                % check against codeInfo
                [pilInterfaceName, pilInterfacePath] = this.getWrapperSourceInfo;                
                pilInterface = fullfile(pilInterfacePath, pilInterfaceName);
                if ~exist(pilInterface, 'file')
                    % this should only happen if xil_interface.c/cpp is manually
                    % deleted
                    isUpToDate = false;
                else
                    % check CodeInfo exists
                    codeInfoPath = this.getCodeInfoPath;
                    if exist(codeInfoPath, 'file')                        
                        codeInfoTimestamp = this.getTimestamp('CodeInfo');
                        pilInterfaceStruct = dir(pilInterface);
                        if codeInfoTimestamp > pilInterfaceStruct.datenum
                            % out of date
                            isUpToDate = false;
                        end
                    else
                        % no CodeInfo => flag as not up to date
                        % (CTDSPI-782) there is no CodeInfo for Subsystem,
                        % use CodeDescriptor when g1983185 is fixed
                        isUpToDate = false;
                    end
                end
            end

            if isUpToDate
                % Check if the top model has changed (we need to query this model for execution
                % profiling settings)
                if ~isempty(topModel) && ...
                        ~strcmp(getTopModelForModelBlockPIL(this), ...
                           topModel)
                    isUpToDate = false;
                end
            end
            
            if isUpToDate
                % Check for changes in code instrumentation
                lOldCodeInstrInfo = this.getSILPILWrapperUtils...
                    .getComponentArgs.getCodeInstrInfo;
                lOldBuildHooks = this.getSILPILWrapperUtils...
                    .getComponentArgs.getBuildHooks;
                lBuildHooks = this.getSILPILWrapperUtils.getInstrumentationSettingsModelBuildHooks();
                lCodeExecutionProfilingEnabled = ...
                    initExecutionProfilingSettings(this.getSILPILWrapperUtils);
                lCodeInstrInfo = this.getClientInterface.getCodeInstrInfo...
                    (this, this.getSILPILWrapperUtils.getInstrumentationSettingsModel,...
                    lBuildHooks, lCodeExecutionProfilingEnabled);
                isUpToDate = isequal(lOldCodeInstrInfo, lCodeInstrInfo) && ...
                    isequal(lOldBuildHooks, lBuildHooks);
            end
                
        end                
        
        function checkWrapperIsNewerThanExportedCode(this)
            % Check host/target wrapper is newer than XRelSourceFile.
            % For the cross-release blocks we use age of buildInfo.mat to 
            % represent the age of the wrapper
            try
                buildInfo = this.getBuildInfoPath;
                % check existence on this file
                rtw.pil.SILPILInterface.errorIfWrapperFileMissing(...
                    buildInfo);
                
                sourceFileTimeStamp = this.getTimestamp('XrelSourceFile');
                buildInfoTimeStamp = this.getTimestamp('BuildInfo');
                % compare timestamps.
                if sourceFileTimeStamp > buildInfoTimeStamp
                    % buildInfo is more recent, wrapper is out of date
                    rtw.pil.ProductInfo.error('xrelimport', ...
                        'ErrorWrapperFileIsOlderThanExportedCode',...
                        this.XRelSourceFile, buildInfo);
                end
            catch origE
                % construct standard error with origE as cause
                rtw.pil.SILPILInterface.addCauseAndThrowNewError(...
                    origE, 'ErrorWrapperOutOfDate');
            end
        end
        
        function checkWrapperIsNewerThanCodeInfo(this)
            % check host/target wrapper is newer than CodeInfo, i.e. we successfully
            % generated a wrapper for this CodeInfo.
            
            % make sure any error is re-thrown as an ErrorWrapperIsNewerThanCodeInfo error
            try                                               
                % See http://inside.mathworks.com/wiki/PIL_Application_Build_Process,
                % "Determining if an Existing PIL Application is Out of Date" for
                % further details.                
                wrapperInfoTgt = this.getWrapperInfoFileName;
                % check existence on this file
                rtw.pil.SILPILInterface.errorIfWrapperFileMissing(...
                    wrapperInfoTgt);
                
                codeInfoTimestamp = this.getTimestamp('CodeInfo');
                % get the stored timestamp
                wrapperInfo = this.getWrapperInfo;
                % compare timestamps.
                if codeInfoTimestamp > wrapperInfo.wrapperTimeStamp
                    % CodeInfo is more recent, wrapper is out of date
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorWrapperFileIsOlderThanCodeInfo', wrapperInfoTgt);
                end
            catch origE
                % construct standard error with origE as cause
                rtw.pil.SILPILInterface.addCauseAndThrowNewError(...
                    origE, 'ErrorWrapperIsNewerThanCodeInfo');
            end
        end                                   
        
        
        function [srcFullPath, srcPath, srcShort, headerFullPath] = ...
                getSourceFileProfiling(this)
            targetLang = this.getSILPILWrapperUtils.getParam('TargetLang');
            [~, sourceExt, sourceFilenameProfiling] = ...
                rtw.pil.SILPILInterface.getWrapperSourceFilenameAndExt(targetLang);
            srcShort = [sourceFilenameProfiling sourceExt];
            srcPath = this.getApplicationDir;
            srcFullPath = fullfile(srcPath, srcShort);

            switch sourceExt
              case '.c'
                headerExt = '.h';
              case '.cpp'
                headerExt = '.hpp';
              otherwise
                assert(true, 'Invalid source file extension %s.', e);
            end
            
            headerFullPath = fullfile(srcPath, [sourceFilenameProfiling headerExt]);
        end
        
        function buildWrapper(this, isPILBlockConfigure, buildSFunctionOnly, ...
                lXilCompInfo, lDefaultMexCompilerKey)
            % Creates the PIL Wrapper. 
            
            % make sure CodeDescriptor is available, it could not be when
            % this method is called during the second simulation in back to
            % back SIL/PIL simulations.
            this.SILPILWrapperUtils.restoreAccessToCodeDescriptorObjs;
            
            % start profiling of the PIL application build
            this.slbuild_profile('SIL APPLICATION BUILD',true);                        
            
            this.slbuild_profile('Check Wrapper is up-to-date', true);                                                                                    
            wrapperOutOfDate = false;            
                                            
            % for an existing SIL/PIL block, we must use ModelLoggingInfo
            % that was cached during the initial creation of the block.
            % That is the only time we can guarantee the ModelLoggingInfo
            % will not have been influenced by the SIL/PIL block itself,
            % e.g. consider what happens when the SIL/PIL block replaces
            % the source subsystem in the original model - the
            % ModelLoggingInfo changes...
            cachedXILBlockModelLoggingInfo = [];
            isSILPILBlock = this.InTheLoopType == rtw.pil.InTheLoopType.Block;
            if isSILPILBlock && ~isPILBlockConfigure
                % we know checkWrapperIsNewerThanCodeInfo has already been
                % called (without try/catch) from SILPILBlock and 
                % therefore, wrapperInfo must be valid...                
                wrapperInfo = this.getWrapperInfo;                
                % initialize based upon wrapperInfo cache
                %
                % Note: cachedXILBlockModelLoggingInfo will only be empty
                % if logging is not enabled (e.g. SILPILWrapperUtils
                % isLoggingEnabled), in which case subsequent calls to 
                % initLoggingData will also be inert.
                cachedXILBlockModelLoggingInfo = wrapperInfo.XILBlockModelLoggingInfo;                
            end
            
            %MATLABSIL Currently always rebuild wrapper function. No
            %concept of incremental code generation in MATLAB Coder anyway
            if this.getInTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn
                % determine if the wrapper is out of date and hence if we need to
                % generate it (e.g. xil_interface.c/cpp)
                try
                    % it must be newer than CodeInfo
                    %
                    % error will be thrown as ErrorWrapperIsNewerThanCodeInfo
                    this.checkWrapperIsNewerThanCodeInfo;
                catch e
                    switch e.identifier
                        case rtw.pil.ProductInfo.message('pilverification', 'ErrorWrapperIsNewerThanCodeInfo')
                            wrapperOutOfDate = true;
                        otherwise
                            rethrow(e);
                    end
                end
            else
                wrapperOutOfDate = true;
            end
            
            % initialize data for signal and state logging which may change
            % between simulation runs
            %
            % Note: always initialize because logging data is not fully 
            % initialized during construction of SILPILWrapperUtils 
            % (optimization).
            %
            % Returns ModelLoggingInfo that may be cached in wrapperUtils.            
            modelLoggingInfo = this.SILPILWrapperUtils.initLoggingData(cachedXILBlockModelLoggingInfo);                                 
            if ~wrapperOutOfDate       
                % We must use settings for the current simulation (not cached
                % settings from a previous build)
                %
                % also initialized during construction of
                % SILPILWrapperUtils 
                this.SILPILWrapperUtils.initCodeInstrumentation;
                initExecutionProfilingSettings(this.SILPILWrapperUtils);
                try
                    this.checkWrapperIsUpToDate(buildSFunctionOnly, lXilCompInfo);
                catch e
                    switch e.identifier
                        case rtw.pil.ProductInfo.message('pilverification', 'ErrorWrapperOutOfDate')
                            wrapperOutOfDate = true;
                        otherwise
                            rethrow(e);
                    end
                end
            end
                        
            this.slbuild_profile('Check Wrapper is up-to-date', false);
            
            if wrapperOutOfDate               
                [~, ...
                    pilInterfaceTarget, ...
                    wrapperInfoTarget, ...
                    allDependencies] = this.getApplicationDependencies...
                    (buildSFunctionOnly, lXilCompInfo);

                lGlobalRegistry = this.getClientInterface.createExecTimeGlobalReg(this);
                if ~isempty(lGlobalRegistry)
                    this.SILPILWrapperUtils.setCodeInstrumentationEnabled(...
                        lGlobalRegistry.getTotalNumberOfProbes > 0 ||...
                        this.SILPILWrapperUtils.isProfilingEnabled);
                    % Evaluate checksum prior to adding the task registry if execution profiling is enabled
                    lCodeInstrumentationChecksum = lGlobalRegistry.getModelInstrumentationChecksum;
                    lProfilingGranularityOnChecksum = lGlobalRegistry.getFeatureChecksum('GranularityOn');
                    lProfilingGranularityOffChecksum = lGlobalRegistry.getFeatureChecksum('GranularityOff');
                end

                % Execution profiling
                if this.SILPILWrapperUtils.isProfilingEnabled                                        
                    [componentName, componentRef] = this.SILPILWrapperUtils.getComponentName;
                    
                    lCodeDir = this.CodeDir;
                    lAnchorFolder = this.getAnchorFolder;
                    if this.getInTheLoopType == rtw.pil.InTheLoopType.MATLABFcn
                        %Until code traceability is supported with MATLAB
                        %Coder XIL, pass an empty relative path to registry
                        lCodeFolderRelative = [];
                    else
                        lCodeFolderRelative = lCodeDir(length(lAnchorFolder)+2:end);
                    end
                    
                    lTaskRegistry = ...
                        coder.profile.TimeProbeComponentRegistry...
                        (componentName,...
                        componentRef,...
                        this.SILPILWrapperUtils.getParam('TargetWordSize'),... 
                        [],...
                        this.SILPILWrapperUtils.getParam('MaxIdLength'),...
                        lCodeFolderRelative);
                    lGlobalRegistry.addRegistries({lTaskRegistry});
                    
                    profilingService = this.getSILPILWrapperUtils.getTargetServices().getProfilingService();
                    lIsProfilingOverheadFilterEnabled = profilingService.isOverheadFilteringActive;
                    lIsProfilingCaptureModeEnabled = ...
                        strcmp(this.SILPILWrapperUtils.CodeProfilingSaveOptions, 'MetricsOnly');
                    if lIsProfilingOverheadFilterEnabled && ...
                            lIsProfilingCaptureModeEnabled
                        DAStudio.error('CoderProfile:ExecutionTime:ClashOverheadAndCaptureMode');
                    end
                    
                    if lIsProfilingCaptureModeEnabled
                        lCaptureRegistry = coder.profile.CaptureProbeComponentRegistry(componentName,...
                            componentRef,...
                            this.SILPILWrapperUtils.getParam('TargetWordSize'),...
                            [],...
                            lCodeFolderRelative);
                        lGlobalRegistry.addRegistries({lCaptureRegistry});
                        
                        lGlobalRegistry.UploadDataInRealTime = false;
                        
                        this.SILPILWrapperUtils.setProfilingCaptureRegistry...
                            (lCaptureRegistry);
                    else
                        lCaptureRegistry = [];
                        lGlobalRegistry.UploadDataInRealTime = true;
                        lGlobalRegistry.TotalTimeProbe = 0;
                    end
                    
                    lTimer = this.SILPILWrapperUtils.getProfilingTimer;  
                    lGlobalRegistry.ProfilingTimer = lTimer;
                    
                    % For SIL and PIL, the application runs in a single thread therefore it can use
                    % a (more accurate) timing method that is not thread-safe.
                    lGlobalRegistry.SingleThreadTiming = true;

                    % The content of the task profiling registry must persist
                    this.SILPILWrapperUtils.setProfilingTaskRegistry...
                        (lTaskRegistry);
                    
                    % if profiling is on, check if the overhead filtering
                    % is active
                    
                    lRunBenchmarkOverhead = false;
                    if lIsProfilingOverheadFilterEnabled && profilingService.isOverheadBenchmarkExecuted
                        lOverheadRegistry = ...
                            coder.profile.OverheadProbeComponentRegistry...
                            (componentName,...
                            componentRef,...
                            this.SILPILWrapperUtils.getParam('TargetWordSize'),...
                            [],...
                            this.SILPILWrapperUtils.getParam('MaxIdLength'),...
                            lCodeFolderRelative);
                        lGlobalRegistry.addRegistries({lOverheadRegistry});
                        this.SILPILWrapperUtils.setProfilingOverheadRegistry...
                            (lOverheadRegistry);
                        lRunBenchmarkOverhead = true;
                    else
                        lOverheadRegistry = [];
                    end                                
                    lGlobalRegistry.FilterProfilingOverhead = lIsProfilingOverheadFilterEnabled;
                    lGlobalRegistry.RunOverheadBenchmark = lRunBenchmarkOverhead;
                else
                    lIsProfilingCaptureModeEnabled = false;
                end
                
                lGlobalRegistry.TargetCollectDataFcnName = ...
                    rtw.pil.SILPILInterface.TargetCodeInstrFcnName;
                
                % Set names of header and source file for instrumentation utility functions
                [lSourceFile, ~, ~, lHeaderFile] = this.getSourceFileProfiling; 
                lGlobalRegistry.SourceFileTargetInterface = lSourceFile;
                lGlobalRegistry.HeaderFileTargetInterface = lHeaderFile;                
                if this.isSIL
                    lGlobalRegistry.TimerHWCounterUnit = coder.profile.private.getHWCounterUnit();
                    this.SILPILWrapperUtils.setProfilingHWCounterUnit(lGlobalRegistry.TimerHWCounterUnit);
                end
                this.slbuild_profile('Generating Host s-function', true);                                
                % generate host s-function
                generateHostSfunction(this, lDefaultMexCompilerKey);
                
                % log profiling measurements of generating Host s-function
                this.slbuild_profile('Generating Host s-function', false);                                
                              
                this.SILPILWrapperUtils.AreEventCallbacksRequired = ~this.IsForSldvAnalysis && ...
                    lGlobalRegistry.areEventCallbacksRequired();
        
                if ~buildSFunctionOnly
                    % We don't want to create xil_interface.c or any of the
                    % other related artifacts when updating model ref
                    % targets or building the S-function only (update
                    % diagram)
                    
                    this.slbuild_profile('Generating pil_interface.c/cpp', true);
                    % generate target xil_interface.c/cpp
                    
                    try
                        targetInterface = this.CodeInterfaceFactory...
                            .createTargetInterface(this.SILPILWrapperUtils, ...
                            pilInterfaceTarget);
                        append = false;
                        callCBeautifier = true;
                        obfuscateCode = this.SILPILWrapperUtils.shouldObfuscateXILWrapperFiles;
                        targetInterface.writeOutput(append, callCBeautifier, obfuscateCode);
                        lTargetWordSize = this.SILPILWrapperUtils.getParam('TargetWordSize');
                        lTargetBitPerChar = this.SILPILWrapperUtils.getParam('TargetBitPerChar');
                        
                        % only create target interface if there is more then one probe
                        if this.SILPILWrapperUtils.isCodeInstrumentationEnabled
                            if lIsProfilingCaptureModeEnabled
                                lGlobalRegistry.updateNumberOfTimeProbes();
                                lCaptureRegistry = this.SILPILWrapperUtils.getProfilingCaptureRegistry;
                                lCaptureRegistry.requestCaptureProbes(lGlobalRegistry.TotalTimeProbe);
                            end
                            lGlobalRegistry.writeTargetInterface(lTargetWordSize, lTargetBitPerChar);
                        end
                    catch targetInterfaceE
                        % creating the target interface modifies CodeInfo
                        % reinitialize CodeInfo objects to return to a default
                        % state
                        try
                            this.getSILPILWrapperUtils.resetCodeInfoUtils;
                        catch reloadE
                            % don't expect this to fail, but if it does,
                            % combine with the original exception
                            combinedE = reloadE.addCause(targetInterfaceE);
                            throw(combinedE);
                        end
                        rethrow(targetInterfaceE);
                    end                    
                    
                    % log profiling measurements of generating
                    % xil_interface.c/cpp
                    this.slbuild_profile('Generating pil_interface.c/cpp', false);
                    
                    % access functions
                    if this.InTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn
                        %Not supported for MATLAB Coder
                        
                        [file, path] = this.getAccessFcnsSourceInfo;
                        accessFcnsFile = fullfile(path, file);
                        accessFcns = rtw.pil.AccessFunctions(...
                            this.SILPILWrapperUtils, ...
                            accessFcnsFile,...
                            targetInterface.getAccessFunctionsData);
                        if rtw.pil.AccessFunctions.exist(this.SILPILWrapperUtils.getCodeInfo)
                            accessFcns.writeOutput(append, callCBeautifier, obfuscateCode);
                        end
                    end
                    
                    % creating the target interface modifies CodeInfo
                    % reinitialize CodeInfo objects to return to a default
                    % state
                    this.getSILPILWrapperUtils.resetCodeInfoUtils;
                    
                    % generate coder assumptions
                    if this.SILPILWrapperUtils.isCoderAssumptionsEnabled
                        generateCoderAssumptions(this, lXilCompInfo);
                    end
                end
                
                this.slbuild_profile('Saving info mat file', true);
                % write the info mat file - we need this information to determine
                % whether the application is up to date
                wrapperInfoData.pilApplicationVersionString = rtw.pil.getPILVersion;
                % store connectivity config info
                wrapperInfoData.targetServicesChecksum = this.SILPILWrapperUtils.getTargetServices().getServiceInstanceIdentifier();
                                

                wrapperInfoData.isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                wrapperInfoData.profilingSaveOpts = this.SILPILWrapperUtils.CodeProfilingSaveOptions;

                wrapperInfoData.lGlobalRegistry = [];
                wrapperInfoData.lCodeInstrumentationChecksum = [];
                wrapperInfoData.lProfilingGranularityOnChecksum = [];
                wrapperInfoData.lProfilingGranularityOffChecksum = [];
                wrapperInfoData.lTaskRegistry = [];
                
                if this.SILPILWrapperUtils.isCodeInstrumentationEnabled
                    wrapperInfoData.lGlobalRegistry = lGlobalRegistry;
                    wrapperInfoData.lCodeInstrumentationChecksum = lCodeInstrumentationChecksum;
                    wrapperInfoData.lProfilingGranularityOnChecksum = lProfilingGranularityOnChecksum;
                    wrapperInfoData.lProfilingGranularityOffChecksum = lProfilingGranularityOffChecksum;
                end
                
                if wrapperInfoData.isExecutionProfilingEnabled
                    wrapperInfoData.lProfilingTaskRegistry = lTaskRegistry;
                    wrapperInfoData.lProfilingOverheadRegistry = lOverheadRegistry;
                    wrapperInfoData.lProfilinglCaptureRegistry = lCaptureRegistry;
                end
                
                loggingData = this.SILPILWrapperUtils.getLoggingData;                
                if ~isempty(loggingData)
                   wrapperInfoData.loggingChecksum = loggingData.getChecksum; 
                else
                   wrapperInfoData.loggingChecksum = []; 
                end                   
                if isSILPILBlock
                    wrapperInfoData.XILBlockModelLoggingInfo = modelLoggingInfo;
                else
                    wrapperInfoData.XILBlockModelLoggingInfo = [];
                end                
                % to differentiate between use of the standalone XIL dir
                % for Top-Model XIL, XIL block and Model block XIL
                % 'Top model' mode, store a flag to make sure a wrapper 
                % rebuild occurs.
                wrapperInfoData.TopLevelSfunctionType = this.getTopLevelSfunctionType();
                % flag to indicate that the code generation report should be
                % regenerated
                wrapperInfoData.reportOutOfDate = true;
                
                % finally, record a timestamp - use the last file to have
                % been generated
                allDepsDirInfo = cellfun(@(x)dir(x), allDependencies, 'UniformOutput', false);
                wrapperInfoData.wrapperTimeStamp = max(cellfun(@(x)x.datenum, allDepsDirInfo));

                % TODO(nbertolo): Could we also remove this?
                %                 Created g1171943 to track this.
                if this.getClientInterface().isMWCodeCoverageEnabled()
                    wrapperInfoData.SlCovEnabled = this.SILPILWrapperUtils.isSlCovEnabled();
                end
                
                % Note: any time we add new data to the PIL wrapper info
                % .mat file we must increment the "localOffset" in method getPILVersion                
                save(wrapperInfoTarget, '-struct', 'wrapperInfoData');                  
                % log profiling measurements of saving the info mat file. This way we
                % can explicitly measure the generation of the host s-function and
                % xil_interface.c/cpp calls
                this.slbuild_profile('Saving info mat file', false);                                
                
                if this.SILPILWrapperUtils.isCodeInstrumentationEnabled && ~buildSFunctionOnly
                    instrInfoData.lGlobalRegistry = lGlobalRegistry;
                    instrInfoData.profChecksum = lGlobalRegistry.getModelInstrumentationChecksum;
                    
                    % generates target specific information
                    
                    % summary metrics
                    saveOpts = this.SILPILWrapperUtils.CodeProfilingSaveOptions;
                    if ~isempty(saveOpts)
                        switch saveOpts
                            case 'MetricsOnly'
                                summaryOnly = true;
                                onTarget = true;
                            case 'SummaryOnly'
                                summaryOnly = true;
                                onTarget = false;
                            case 'AllData'
                                summaryOnly = false;
                                onTarget = false;
                            otherwise
                                assert(false, 'Invalid value for execution profiling save options');
                        end
                        instrInfoData.targetInfo.summaryOnly = summaryOnly;
                        instrInfoData.targetInfo.onTargetOnly = onTarget;
                    end
                    
                    % maximum size for timer value
                    lTimer = this.SILPILWrapperUtils.getProfilingTimer;
                    lTimerType = lTimer.getTimerType;
                    instrInfoData.targetInfo.timerTypeMaxValue = intmax(lTimerType);
                    
                    lTargetWordSize = this.SILPILWrapperUtils.getParam('TargetWordSize');
                    identifierMaxVal = coder.profile.ExecTimeConfig...
                        .getIdentifierMaxValueFromTargetWordSize(lTargetWordSize);
                    instrInfoData.targetInfo.identifierMaxVal = identifierMaxVal;
                    
                    timerInBytes = rtw.connectivity.TypeUtils.builtinHostByteSize(lTimerType);
                    instrInfoData.targetInfo.timerInBytes = uint8(timerInBytes);
                    timerTypeName = lTimerType;
                    instrInfoData.targetInfo.timerTypeName = timerTypeName;
                    tps = this.SILPILWrapperUtils.getProfilingTimer.getTicksPerSecond;
                    if isempty(tps)
                        tps = 0;
                    end
                    if ~isempty(lGlobalRegistry.TimerHWCounterUnit)
                        instrInfoData.targetInfo.HWCounterUnit = lGlobalRegistry.TimerHWCounterUnit;
                    end
                    instrInfoData.targetInfo.ticksPerSecond = uint64(tps);

                    appDir = this.getApplicationDir;
                    coder.profile.CoderInstrumentationInfo.writeInfo(...
                        appDir, ...
                        instrInfoData);
                end
                    
            end                                                
            
            if ~buildSFunctionOnly
                this.SILPILWrapperUtils.initTaskProfilingStreaming;
                % update the cached SILPILInterface
                handler = coder.connectivity.PILDataHandler.createHandler(this.InTheLoopType);
                handler.setPILData(this.Owner, this);
            end
            
            this.slbuild_profile('SIL APPLICATION BUILD',false);                        
        end
        
        function buildApplication(this, lXilCompInfo, lDefaultMexCompilerKey)
            % Creates the PIL Application Executable.
            
            % make sure CodeDescriptor is available, it could not be when
            % this method is called during the second simulation in back to
            % back SIL/PIL simulations.
            this.SILPILWrapperUtils.restoreAccessToCodeDescriptorObjs;
            
            [extraSrcFile, ~, ~, extraHdrFile] = this.getSourceFileProfiling;
                
            % Create new buildInfo for the PIL application            
            targetServices = this.getSILPILWrapperUtils().getTargetServices();
            
            buildService = targetServices.getBuildDependencyService();
            buildInfo = buildService.createConnectivityBuildInfo();
            
            rtioStreamAppService = targetServices.getRtIOStreamApplicationService();    

            % Add buffer size defines            
            rtw.pil.BuildInfoHelpers.addTargetSendAndReceiveBufferSizeDefines(buildInfo, ...
                rtioStreamAppService.getSendBufferSize(), rtioStreamAppService.getReceiveBufferSize());
                
            if this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                    this.SILPILWrapperUtils.isProfilingEnabled                              
                buildInfo.addSourceFiles(extraSrcFile, '', 'Sfcn');
                buildInfo.addIncludeFiles(extraHdrFile, '', 'Sfcn');
            else
                buildSrcFiles = buildInfo.getSourceFiles(true, true);
                srcKeepIdx = true(size(buildInfo.Src.Files));
                for i = 1 : length(buildSrcFiles)
                    if strcmp(buildSrcFiles{i}, extraSrcFile)
                        delete(extraSrcFile);
                        srcKeepIdx(i) = false;
                        break;
                    end
                end
                buildInfo.Src.Files = buildInfo.Src.Files(srcKeepIdx);
                
                buildIncFiles = buildInfo.getIncludeFiles(true, true);
                incKeepIdx = true(size(buildInfo.Inc.Files));
                for i = 1 : length(buildIncFiles)
                    if strcmp(buildIncFiles{i}, extraHdrFile)
                        delete(extraHdrFile);
                        incKeepIdx(i) = false;
                        break;
                    end
                end                
                buildInfo.Inc.Files = buildInfo.Inc.Files(incKeepIdx);
                
            end

            if (this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                    this.SILPILWrapperUtils.isProfilingEnabled)...
                    && ~this.InstrumentationInfoAddedToBuildInfo
                this.addCodeInstrAppServiceSources(true, buildInfo);
                this.InstrumentationInfoAddedToBuildInfo = true;
            end
            if ~(this.SILPILWrapperUtils.isCodeInstrumentationEnabled ||...
                    this.SILPILWrapperUtils.isProfilingEnabled)...
                    && this.InstrumentationInfoAddedToBuildInfo
                this.addCodeInstrAppServiceSources(false, buildInfo);
                this.InstrumentationInfoAddedToBuildInfo = false;
            end                        
            
            % Add or remove timer source files and code instrumentation app
            % service source files to buildInfo
            if this.SILPILWrapperUtils.isProfilingEnabled ...
                    && ~this.TimerInfoAddedToBuildInfo
                this.addTimerSources(true, buildInfo);
                this.TimerInfoAddedToBuildInfo=true;
            end
            if ~this.SILPILWrapperUtils.isProfilingEnabled ...
                    && this.TimerInfoAddedToBuildInfo
                this.addTimerSources(false, buildInfo);
                this.TimerInfoAddedToBuildInfo=false;
            end
            
            % access functions
            if this.InTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn && ...
                    rtw.pil.AccessFunctions.exist(this.SILPILWrapperUtils.getCodeInfo)
                [file, path] = this.getAccessFcnsSourceInfo;
                fileName = fullfile(path, file);
                buildInfo.addSourceFiles(...
                    fileName, '', 'Sfcn');
            end
            
            if (this.InTheLoopType == rtw.pil.InTheLoopType.MATLABFcn)
                targetLang = this.SILPILWrapperUtils.getParam('TargetLang');
                gpuConfig = this.SILPILWrapperUtils.getParam('GpuConfig');
                isGpu = ~isempty(gpuConfig) && gpuConfig.Enabled;
                if isGpu
                    extension = '.cu';
                else
                    switch targetLang
                        case 'C'
                            extension = '.c';
                        case 'C++'
                            extension = '.cpp';
                        otherwise
                            assert(false, 'Unknown TargetLang: %s', targetLang);
                    end
                end
                fcnName = this.getSILPILWrapperUtils.getComponentName;
                srcPath = this.getCodeDir;
                srcFilePath = fullfile(srcPath, 'target');
                buildInfo.addSourceFiles(sprintf('_coder_%s_target%s', fcnName, extension), srcFilePath, 'Sfcn');
                buildInfo.addIncludePaths(srcFilePath);
                buildInfo.addIncludeFiles(sprintf('_coder_%s_target.h', fcnName), srcFilePath);
            end
            
            % sets the current MemUnit defines
            this.setMemUnitDefines(buildInfo);
            
            % log profiling measurements of a_buildApplication
            this.slbuild_profile('Building the SIL Application', true);
            % build the application
            %
            % application build is always issued because we don't track all possible
            % dependencies, such as referenced models, etc.   It's up to the
            % connectivity API build process to manage such dependencies and only
            % rebuild when necessary.                        
            %
            % protected call through API
            
                    
            % Flag indicating if code under test is a shared library
            isSharedLib = isDLLTarget(...
                getClientInterface(this), getConfigInterface(getSILPILWrapperUtils(this)));
            if isSharedLib
                % If testing a shared library, need to enforce use of absolute path instead of a
                % relative path. Issue with a relative path is that the shared library may not
                % be found at runtime.
                modelRefLibPathArgs = {
                    'ModelRefLibPathFormat', coder.make.internal.ModelRefLibPathFormat.ABSOLUTE};
            else
                modelRefLibPathArgs = {};
            end
            
            % For Coder Target builds, content of auxiliary makefile is a ...
            % required argument to the compile stage
            lAuxiliaryMakefileContent = coder.internal.getCoderTargetAuxMakefileContent( ...
                this.getSILPILWrapperUtils().getComponentArgs().getComponentCodePath());
            
            % Data needed to determine the pre-compiled library suffix
            lIsPurelyIntegerCode = ...
                strcmp(getParam(this.SILPILWrapperUtils, 'PurelyIntegerCode'), 'on');
            targetLibSuffixFromCs = getParam(this.SILPILWrapperUtils, 'TargetLibSuffix');

            % Data required (mainly from configset) to determine build tool
            % configuration settings
            lPortableWordSizes = getParam(this.SILPILWrapperUtils, 'PortableWordSizes');
            lIsSILAndPWS = this.isSIL && strcmp(lPortableWordSizes, 'on');
            
            try
                forceDebuggingEnabledFlag = this.SILPILWrapperUtils.SILDebuggingInfo.IsEnabled || coder.internal.connectivity.featureOn('SILMemoryCheckTool');
                
                options = rtw.pil.SILPILBuildOptions();
                options.ComponentArgs = this.getSILPILWrapperUtils().getComponentArgs();
                options.XilCompInfo = lXilCompInfo;
                options.LibSuffixFromConfigSet = targetLibSuffixFromCs;
                options.IsPurelyIntegerCode = lIsPurelyIntegerCode;
                options.IsSILAndPWS = lIsSILAndPWS;
                options.ForceDebuggingEnabled = forceDebuggingEnabledFlag;
                options.CodeBuildParams = [{'CoderTargetAuxMakeContent', lAuxiliaryMakefileContent } ...
                                             modelRefLibPathArgs ];
                                        
                buildManager = rtw.pil.ApplicationBuildManager();
                
                buildStrategy = rtw.pil.getCustomBuildStrategy(buildManager, targetServices);
                
                % Build the application executable
                pilExePath = buildManager.build(buildInfo, options, targetServices, buildStrategy);
                                                 
                this.ApplicationExecutable = pilExePath;
                                 
            catch userException
                rtw.pil.SILPILInterface.throwImplementationError(userException);                
            end                        
                        
            wrapperInfo = this.getWrapperInfo;
            
            % make sure the application is up to date relative to the wrapper
            this.checkApplicationIsUpToDate(pilExePath, wrapperInfo.wrapperTimeStamp);
            
            % log profiling measurements of a_buildApplication
            this.slbuild_profile('Building the SIL Application', false);
            
            % now BuildInfo has been fully processed, 
            % update the code generation ReportInfo if required                        
            if ~this.IsModelProtected
                this.getClientInterface.updateReport(this, buildInfo, wrapperInfo.reportOutOfDate, ...
                    lDefaultMexCompilerKey);
            end
            % update the cached SILPILInterface
            handler = coder.connectivity.PILDataHandler.createHandler(this.InTheLoopType);
            handler.setPILData(this.Owner, this);
        end                                                
                
        function setOwner(this, owner)
            % set owner block of this object            
            this.Owner = owner;
        end
        
        function owner = getOwner(h)
            % get owner block of this object
            owner = h.Owner;
        end
        
        function slprjInternalPath = getSlprjInternalPath(this)
           slprjInternalPath = this.SlprjInternalPath; 
        end             
        
        function codeDescriptorPath = getCodeDescriptorPath(this)
            codeDir = this.CodeDir;
            codeDescriptorPath = fullfile(codeDir, 'codedescriptor.dmr');
        end
        
        function buildInfoPath = getBuildInfoPath(this)
            buildInfoPath = fullfile(this.getCodeDir, this.getXilSubDirName, ...
                'buildInfo.mat');
        end
        
        function codeInfoPath = getCodeInfoPath(this)
            % return path to CodeInfo .mat file
            codeDir = this.CodeDescriptorDir;
            % path to the codeInfo object
            switch this.getInTheLoopType
                case {rtw.pil.InTheLoopType.Block, ...                        
                        rtw.pil.InTheLoopType.MATLABFcn, ...
                        rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                        rtw.pil.InTheLoopType.Subsystem}
                    codeInfoPath = fullfile(codeDir, 'codeInfo.mat');
                case rtw.pil.InTheLoopType.ModelBlock
                    codeInfoPath = fullfile(codeDir, [this.getRootModel '_mr_codeInfo.mat']);
                otherwise
                    assert(false, 'InTheLoopType not Supported');
            end
        end
        
        function codeInterfaceFactory = getCodeInterfaceFactory(this)
           codeInterfaceFactory = this.CodeInterfaceFactory; 
        end  

        function MATLABCoderProject = getMATLABCoderProject(this)
            MATLABCoderProject = this.InfoStruct.project; 
        end
        
        %Return any cached away customIncludes from the target build
        %This method is needed to solve G1020690, but should be removed
        %when G1018026 has been implemented.
        function customIncludes = getMATLABCoderCustomIncludes(this)
            customIncludes = this.InfoStruct.customIncludes;
        end
        
        function clientInterface = getClientInterface(this)
           clientInterface = this.ClientInterface; 
        end
        
        function [pilSfun, ...
                pilInterface, ...
                wrapperInfo, ...
                allDependencies] = getApplicationDependencies...
                (this, buildingSFunctionOnly, lXilCompInfo)
            % GETAPPLICATIONDEPENDENCIES: Returns files that the PIL application is dependent on
            %
            if nargin < 2
                buildingSFunctionOnly = false;
            end
                        
            % sfunction binary
            [sfunName, ~, sfunBinDir] = this.getPILSfunction;
            pilSfun = fullfile(sfunBinDir, [sfunName '.' mexext]);
            % xil_interface.c/cpp
            [pilInterfaceName, pilInterfacePath] = this.getWrapperSourceInfo;
            pilInterface = fullfile(pilInterfacePath, pilInterfaceName);
            % wrapperInfo.mat
            wrapperInfo = this.getWrapperInfoFileName;
            
            % cell array of filenames
            if buildingSFunctionOnly
                allDependencies = {pilSfun};
                return;
            end
            
            allDependencies = [{pilSfun}, {pilInterface}];
            
            % coder assumptions files
            if this.SILPILWrapperUtils.isCoderAssumptionsEnabled
                [caSrcFiles, caLibrary] = coder.assumptions.CoderAssumptions.getDependencies( ...
                    this.getCodeGenModel, this.CodeDescriptorDir, ...
                    this.SILPILWrapperUtils.getConfigInterface, this.isSIL, ...
                    lXilCompInfo);
                allDependencies = [allDependencies, caSrcFiles, caLibrary];
            end
            
            % profiling files
            if this.SILPILWrapperUtils.isProfilingEnabled
                [instrumSourceFile, ~, ~, instrumHeaderFile] = this.getSourceFileProfiling;
                allDependencies = [allDependencies, {instrumSourceFile}, {instrumHeaderFile}];
            end
            
            % access functions file
            if rtw.pil.AccessFunctions.exist(this.SILPILWrapperUtils.getCodeInfo)
                [file, path] = this.getAccessFcnsSourceInfo;
                accessFcnsFile = fullfile(path, file);
                allDependencies = [allDependencies, {accessFcnsFile}];
            end
        end      
        
        function value = getBlockPath(this)
            value = this.BlockPath;
        end
        
        function [mf0, parser] = getMF0ModelInfo(this)
            assert(~this.getInTheLoopType ~= rtw.pil.InTheLoopType.MATLABFcn, 'MF0 not applicable to MATLAB Coder');
            lUsingResourcesDir = this.isXRelBlock && this.UsingResourcesDir;
            [mf0, parser] = this.ClientInterface.getMF0ModelInfo( ...
                this.CodeDescriptorDir, ...
                this.getInTheLoopType, ...
                lUsingResourcesDir);
        end
    end
    
    methods (Static)                
        function throwImplementationError(userException)
            % construct standard implementation error with userException cause
            [pilId, pilMessage] = rtw.pil.ProductInfo.message('pilverification', ...
                'PILImplementationError');
            pilException = MException(pilId, ...
                pilMessage);
            pilException = pilException.addCause(userException);
            throw(pilException);
        end
        
        function isHostLittleEndian = isHostLittleEndian           
            [~, ~, hostEndian] = computer;
            switch hostEndian
                case 'L'
                    isHostLittleEndian = true;
                case 'B'
                    isHostLittleEndian = false;
                otherwise
                    assert(false, 'Unexpected hostEndian: %s', hostEndian);
            end                        
        end                      
          
          
        function isSILDebugEnabledAndSILExeAlive = sfunctionIsSILDebugEnabledAndSILExeAlive(getSILPILInterfaceFcnStr, ...
                                                            inTheLoopType, blockPath)
            
            isSILDebugEnabledAndSILExeAlive = 0;
            
            % retrieve the PIL interface                        
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);            
            
            % Return early if we are not doing SIL debugging, check the cached value of SILDebuggingEnabled
            if ~pilInterface.isSIL || ~pilInterface.getSILPILWrapperUtils().SILDebuggingInfo.IsEnabled
                return;
            end
            
            % determine if the SIL executable is still alive
            applicationServices = pilInterface.getTargetServices().getRtIOStreamApplicationService();
            
            % query to see if the application is running, the app service
            % returns a logical, but mex expects this to be a double.
            isSILDebugEnabledAndSILExeAlive = double(applicationServices.getIsRunning());

            % used by internal testing to automate stepping through the
            % MSVC debugger
            if isSILDebugEnabledAndSILExeAlive && strcmp(getenv('DebugSILWithMSVCTesting'),'1') && ispc
                silDebugger = applicationServices.getDebugger();
                mSVCSvr = silDebugger.getMSVCSrvrHandle;

                blocking = false;
                silDebugger.pollMSVCServer(@() mSVCSvr.get('DTE').get('Debugger').invoke('Go', blocking));
                
                % log some data into a file in the current dir
                fileName = 'SILDebugTestLogs.txt';                
                fid = fopen(fileName, 'a');                                
                fwrite(fid, sprintf('Hit Breakpoint\n'));
                fclose(fid);
            end
        end
        
        function rootLoggingPath = sfunctionInitializeHook(getSILPILInterfaceFcnStr, inTheLoopType, ...
                blockPath, ...
                topModel)
            % SFUNCTIONINITIALIZEHOOK - hook for C s-function to call into M
            % initialization code
            
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);
            
            % validate PIL Interface
            if isempty(pilInterface)
                switch inTheLoopType
                    case {rtw.pil.InTheLoopType.ModelBlock, ...
                          rtw.pil.InTheLoopType.ModelBlockStandalone}
                        assert(false, 'No PIL Interface object for Model block PIL');  
                    otherwise
                        rtw.pil.ProductInfo.error('pilverification', 'PILInterfaceMissing');    
                end                                    
            end            
            rootLoggingPath = pilInterface.getClientInterface.initializeHookBody(topModel, blockPath, inTheLoopType);
        end
        
        function executionProfileCallback(getSILPILInterfaceFcnStr, inTheLoopType, blockPath, rootModel, profData) 
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);
            
            pilInterface.getClientInterface.executionProfileCallback(pilInterface, blockPath, profData, rootModel, inTheLoopType);           
        end

        function executionCodeCoverageCallback(getSILPILInterfaceFcnStr, inTheLoopType, blockPath, rootModel, covData) 
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);
            
            pilInterface.getClientInterface.executionCodeCoverageCallback(pilInterface, blockPath, covData, rootModel, inTheLoopType);
        end
        
        function executionProfileStartCallback(getSILPILInterfaceFcnStr, inTheLoopType, blockPath, rootModel) 
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);

            pilInterface.getClientInterface.executionProfileStartCallback(pilInterface, blockPath, rootModel, inTheLoopType);                        
        end
        
        function portNum = sfunctionGetServerPortNumber(getSILPILInterfaceFcnStr, inTheLoopType, blockPath)
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);
            
            % get the server port number
            appService = pilInterface.getTargetServices().getRtIOStreamApplicationService();
            rtioStream = appService.getRtIOStreamInstance();
            assert(isa(rtioStream, 'rtw.connectivity.RtIOStreamWrapper'), ... 
                'rtioStream should be an RtIOStreamWrapper (i.e. using host library rtiostream) in order to retrieve args list and port argument');
            args = rtioStream.getOpenRtIOStreamArgList();
            portArgIdx = find(strcmp(args, '-port'), 1);
            assert(~isempty(portArgIdx), 'Communicator args should have a -port argument!');
            portNum = str2double(args(portArgIdx+1));
        end
        
        function [rtIOStreamLib, ...
                rtIOStreamMATLABObject, ...
                rtIOStreamStreamID, ...
                rtIOStreamRecvTimeout, ...
                rtIOStreamSendTimeout] = sfunctionGetRtIOStreamInfoHook(getSILPILInterfaceFcnStr, inTheLoopType, ...
                blockPath)
            % SFUNCTIONGETRTIOSTREAMINFOHOOK - hook for C s-function to retrieve
            %                                  rtIOStream information.
                                    
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);
            
            targetService = pilInterface.getSILPILWrapperUtils.getTargetServices();
            rtioStreamAppService = targetService.getRtIOStreamApplicationService();
            rtIOStream = rtioStreamAppService.getRtIOStreamInstance();
            if isa(rtIOStream, 'rtw.connectivity.RtIOStreamWrapper')
                rtIOStreamLib = rtIOStream.getRtIOStreamLib();
            else
                % not required
                rtIOStreamLib = '';
            end
            rtIOStreamMATLABObject = rtIOStream;
            rtIOStreamStreamID = rtIOStream.getStreamID();
            
            % get the cached value of SILDebuggingEnabled
            SILDebuggingEnabled = pilInterface.getSILPILWrapperUtils.SILDebuggingInfo.IsEnabled;
            if SILDebuggingEnabled
                % reduce the timeout so the sfunction checks frequently
                % whether the SIL application is still alive
                rtIOStreamRecvTimeout = 1;
                rtIOStreamSendTimeout = 1;
            else
                rtIOStreamRecvTimeout = rtIOStream.getRecvTimeout;
                rtIOStreamSendTimeout = rtIOStream.getSendTimeout;
            end
        end
       
        function sfunctionPILStartHook(getSILPILInterfaceFcnStr, inTheLoopType, ...
                                       blockPath, ioDataType)                                              
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);                          
                                              
            % start profiling of PIL_INIT_COMMAND
            pilInterface.slbuild_profile('XIL_INIT_COMMAND', true);
            
            % initialize internal data of the Launcher
            % some internal Launcher implementations need the
            % blockHandle (e.g. Link TS)
            %
            % Note: for PIL block, blockHandle can change after
            % the PIL block is created if the PIL block is
            % copied.   Setting the blockHandle here is late
            % enough to make sure we capture its
            % correct value at the start of the cosimulation.
            
            % Dispatch any build hooks attached to the model
            componentArgs = pilInterface.getSILPILWrapperUtils().getComponentArgs();
            targetServices = pilInterface.getSILPILWrapperUtils().getTargetServices();
            rtioStreamAppService = targetServices.getRtIOStreamApplicationService();  
            rtioStream = rtioStreamAppService.getRtIOStreamInstance(); 
            
            pilInterface.getClientInterface.dispatchBuildHook(componentArgs, ...
                                                              'before_on_target_execution');                                    
            
            % log profiling measurements of startApplication
            pilInterface.slbuild_profile('startApplication', true);
            % start application
            topContext = ...
                pilInterface.getClientInterface.getTopContext(blockPath, pilInterface);
            pilInterface.startApplication(rtioStreamAppService, topContext);
            
            % log profiling measurements of startApplication
            pilInterface.slbuild_profile('startApplication', false);
            
            % log profiling measurements of initCommunications
            pilInterface.slbuild_profile('initCommunications', true);
            rtioStream.preOpen();
            
            if ~rtw.connectivity.Utils.isCommServiceAPIEnabled
                rtioStream.open(ioDataType);
            end
            
            % log profiling measurements of initCommunications
            pilInterface.slbuild_profile('initCommunications', false);                        
            
            %
            % call legacy "startCommands" hook exposed by arduino.Communicator
            %            
            % log profiling measurements of startCommands
            pilInterface.slbuild_profile('startCommands', true);
            rtioStream.postOpen();            
            pilInterface.slbuild_profile('startCommands', false);
                        
            % end of profiling PIL_INIT_COMMAND
            pilInterface.slbuild_profile('XIL_INIT_COMMAND', false);
            
            % At this point the PIL application has been started.
            % The PIL s-function will now process mdlOutputs. After
            % running mdlOutputs, it will call
            % PIL_TERMINATE_COMMAND. The time between these 2
            % profiling points will be monitored.
            pilInterface.slbuild_profile(...
                'host/target cosimulation',...
                true);                        
        end
        
    	% returns whether Fast Restart is enabled on the top model 
        function isFastRestartEnabled = sfunctionIsFastRestartEnabled(topModel)
            isFastRestartEnabled = Simulink.internal.FastRestart.isEnabled(topModel);
        end           
        
        function sfunctionPILPauseHook(getSILPILInterfaceFcnStr, inTheLoopType, blockPath)
            % SFUNCTIONPILPAUSEHOOK - hook for C s-function to call into MATLAB
            
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);                          

            componentArgs = pilInterface.getSILPILWrapperUtils.getComponentArgs;

            pilInterface.getClientInterface.dispatchBuildHook(componentArgs, 'after_on_target_pause');                                        
        end       
        
        function sfunctionPILStopHook(getSILPILInterfaceFcnStr, inTheLoopType, blockPath)
            % SFUNCTIONPILSTOPHOOK - hook for C s-function to call into M
            %
            
            % retrieve the PIL interface
            pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                getSILPILInterfaceFcnStr, inTheLoopType, blockPath);                          
            
            % Build hooks and associated build environment variables must be cleaned up both
            % in the event of an error and on normal termination. The following
            % relies on the assumption that if the before_on_target_execution
            % hook has been invoked from inside sfunctionPILStartHook then it is
            % guaranteed that we execute this code in sfunctionPILStopHook.
            componentArgs = pilInterface.getSILPILWrapperUtils.getComponentArgs;
            buildHooksCleanup = onCleanup(@()setBuildHookHandles(componentArgs, []));   
            
            try                                                                                
                % PIL_TERMINATE may be called under the following
                % conditions:
                %
                % 1) Normal termination at the end of cosimulation
                % 2) An error occurred during cosimulation (s-fun cleanup)
                %
                % We do not distinguish between these two conditions
                pilInterface.slbuild_profile(...
                    'host/target cosimulation',...
                    false);
                try                    
                    % start profiling of PIL_TERMINATE_COMMAND
                    pilInterface.slbuild_profile('XIL_TERMINATE_COMMAND', true);
                                                           
                    rtioStreamAppService = pilInterface.getSILPILWrapperUtils()... 
                                .getTargetServices().getRtIOStreamApplicationService();
                            
                    rtioStream = rtioStreamAppService.getRtIOStreamInstance();
                    
                    % log profiling measurements of closeCommunications
                    pilInterface.slbuild_profile('closeCommunications', true);
                    % close communications
                    if ~rtw.connectivity.Utils.isCommServiceAPIEnabled
                          rtioStream.close();
                    end
                    
                    % log profiling measurements of closeCommunications
                    pilInterface.slbuild_profile('closeCommunications', false);
                    
                    % log profiling measurements of stopApplication
                    pilInterface.slbuild_profile('stopApplication', true);
                    % stop the application
                    rtioStreamAppService.stop();
                    
                    % log profiling measurements of stopApplication
                    pilInterface.slbuild_profile('stopApplication', false);
                    
                    % Dispatch any build hooks attached to the model                    
                    pilInterface.getClientInterface.dispatchBuildHook(componentArgs, ...
                                                              'after_on_target_execution');                                        
                    
                    % end of profiling PIL_TERMINATE_COMMAND
                    pilInterface.slbuild_profile('XIL_TERMINATE_COMMAND', false);
                catch eOriginal
                    % attempt to cleanup so resources are freed                    
                    try
                        % close communications
                          rtioStreamAppService.close();
                    catch eCloseComms %#ok<NASGU>
                        % ignore errors during cleanup
                    end
                    try
                        % stop the application
                        rtioStreamAppService.stop();
                    catch eStopApp %#ok<NASGU>
                        % ignore errors during cleanup
                    end
                    % rethrow the original error
                    rethrow(eOriginal);
                end
            catch userException
                if strcmp(userException.identifier, 'PIL:pil:SILValgrindMemoryErrorDetected')
                    % throw the Valgrind memory error directly, this is to
                    % avoid showing only the standard implementation error
                    % in the MATLAB Coder workflow (the error is thrown
                    % during atExit and is displayed as a warning)
                    throw(userException);
                else
                    % construct standard implementation error with userException cause
                    [pilId, pilMessage] = rtw.pil.ProductInfo.message('pilverification', ...
                        'PILImplementationError');
                    pilException = MException(pilId, ...
                        pilMessage);
                    pilException = pilException.addCause(userException);
                    throw(pilException);
                end 
            end
        end
        
        function throwMException(mException)                        
            % if multiple exceptions, push each into a parent exception
            if numel(mException) > 1
                parentException = MException('MATLAB:MException:MultipleErrors', ...
                    message('PIL:pil:MultipleErrors'));
                for i = 1:numel(mException)
                    parentException = parentException.addCause(mException{i});
                end
            else
                parentException = mException{1};
            end
            throw(parentException);
        end
        
        % throw the appropriate error for a communications send failure
        function handleRtIOStreamSendError(timeoutOccurred, timeoutValue)            
            errorCauses = DAStudio.message('Connectivity:target:ErrorSendReceiveCauses');
            if timeoutOccurred
                DAStudio.error('Connectivity:target:ErrorSendTimeout', uint32(timeoutValue), errorCauses);                 
            else
                DAStudio.error('Connectivity:target:ErrorSendingData', errorCauses);                 
            end                       
        end
        
        % throw the appropriate error for a communications recv failure
        function handleRtIOStreamRecvError(timeoutOccurred, timeoutValue)
            errorCauses = DAStudio.message('Connectivity:target:ErrorSendReceiveCauses');
            if timeoutOccurred
                DAStudio.error('Connectivity:target:ErrorRecvTimeout', uint32(timeoutValue), errorCauses);
            else
                DAStudio.error('Connectivity:target:ErrorReceivingData', errorCauses);                 
            end            
        end
        
        function [filename, extension, filenameInstrumentation] = getWrapperSourceFilenameAndExt(targetLang)     
            filename = 'xil_interface';
            filenameInstrumentation = 'xil_instrumentation';
            switch targetLang
                case 'C'
                    extension = '.c';                    
                case 'C++'
                    extension = '.cpp';
                otherwise
                    assert(false, 'Unknown TargetLang: %s', targetLang);
            end
        end                
    end
    
    methods (Static, Access = 'private')                            
        function validateWrapperVersion(wrapperInfo)
            % check version string
            %
            % Note: any time we add new data to the PIL wrapper info
            % .mat file we must increment the "localOffset" in getPILVersion.m
            pilAppVer = rtw.pil.getPILVersion;
            if ~strcmp(pilAppVer, wrapperInfo.pilApplicationVersionString)
                % version mismatch
                rtw.pil.ProductInfo.error('pilverification', ...
                    'ErrorWrapperIsIncomplete');
            end
        end
        
        function addCauseAndThrowNewError(origCause, newErrId)
            % construct standard error with origE as cause
            [newId, newMessage] = rtw.pil.ProductInfo.message(...
                'pilverification', newErrId);
            newException = MException(newId, newMessage);
            newException = newException.addCause(origCause);
            throw(newException);
        end
        
        function errorIfWrapperFileMissing(filename)
            if ~exist(filename, 'file')
                rtw.pil.ProductInfo.error('pilverification', ...
                    'ErrorWrapperMissingFile', filename);
            end
        end

        % Displays a warning [if needed] for the special case of the 
        % memoryAtStartup and dynamicMemoryAtStartup assumption checks.
        function coderAssumptionMemoryAtStartup(memoryMsg, model, isSLConfig, ...
                                interfaceStr, inTheLoopType, blockPath)
            % There is no relevant option in MATLAB Coder. Only
            % process this message in the case of Simulink Coder.
            if isSLConfig
                assert(~any(ismember({memoryMsg.Status}, 'Fail')), 'Unexpected status for MemoryAtStartup validation');
                hasWarningStatus = ismember({memoryMsg.Status}, {'Warning'});
                
                % Generate the warning only if there is a Warning status
                if any(hasWarningStatus)
                    extMemCode = 'ZeroExternalMemoryAtStartup';
                    removeExternalZeroInit = strcmpi(get_param(model, extMemCode), 'off');
                    extSettingDescription = ...
                        rtw.pil.SILPILInterface.getParameterInfo(model, extMemCode);
                    
                    intMemCode = 'ZeroInternalMemoryAtStartup';
                    removeInternalZeroInit = strcmpi(get_param(model, intMemCode), 'off');
                    intSettingDescription = ...
                        rtw.pil.SILPILInterface.getParameterInfo(model, intMemCode);
                    
                    % Warn if any zero initialization code is being removed.
                    if removeInternalZeroInit && removeExternalZeroInit
                        changeSettings = strjoin({extMemCode, intMemCode}, ', ');
                        changeDescription = strjoin({extSettingDescription, intSettingDescription}, ', ');
                    elseif removeExternalZeroInit
                        changeSettings = extMemCode;
                        changeDescription = extSettingDescription;
                    elseif removeInternalZeroInit
                        changeSettings = intMemCode;
                        changeDescription = intSettingDescription;
                    else
                        % no zero initialization being removed -> no need to warn.
                        return
                    end
                    
                    % Create the base warning for memory tests failures.
                    pilInterface = coder.connectivity.ClientInterface.getSILPILInterface(...
                        interfaceStr, inTheLoopType, blockPath);
                    
                    % When generating C++ Classes, the memory initialization
                    % parameters are read-only hence we consider the component
                    % read-only.
                    isReadOnly = pilInterface.getSILPILWrapperUtils.isComponentReadOnly || pilInterface.getSILPILWrapperUtils.isCPPEncap;
                    
                    readOnlyActionID = {'MemSugg'};
                    fullAccessActionID = {'MemFix'};
                    memoryWarn = coder.connectivity.AccessDependentDiagnostic(...
                        'Connectivity:target:CoderAssumptionsMemStartupFixit', ...
                        readOnlyActionID, ...
                        fullAccessActionID, ...
                        model, ...
                        changeSettings, ...
                        changeDescription);
                    
                    % Loop over the messages with Warning status
                    warningMsg = memoryMsg(hasWarningStatus);
                    
                    for i = 1:numel(warningMsg)
                        actualValue = warningMsg(i).Actual;
                        switch warningMsg(i).Name
                            case 'MemoryAtStartup'
                                targetLang = get_param(model, 'TargetLang');
                                causeMessage = message('Connectivity:target:CoderAssumptionsGlobalMem',['0x' dec2hex(str2double(actualValue))],targetLang);
                            case 'DynamicMemoryAtStartup'
                                causeMessage = message('Connectivity:target:CoderAssumptionsDynamicMem',['0x' dec2hex(str2double(actualValue))]);
                            otherwise
                                assert(false,'Unexpected warning message for MemoryAtStartup tests');
                        end
                        
                        memoryWarnCause = MSLDiagnostic(causeMessage);
                        memoryWarn.Causes{i} = memoryWarnCause;
                    end
                
                    % Display the warning with the "fixit" and causes messages.
                    memoryWarn.reportWarning(isReadOnly);
                end
            end
        end
        
                
        function newExc = coderAssumptionHWSettings(msg, model, isSLConfig, hyperlink, previousExc)
            % return an mException obj caused by a mismatch in the
            % hardware settings
            
            % initialize the return argument in case of no exception is
            % created
            newExc = previousExc;
            
            % For Simulink Coder, include the parameter name and the prompt
            if isSLConfig && bdIsLoaded(model)
                [paramDescription, paramName] = ...
                    rtw.pil.SILPILInterface.getParameterInfo(model, msg.Name);
                % Some prompts have colons, strip them.
                paramName = sprintf('%s (%s)', paramName, ...
                    strrep(paramDescription, ':', ''));
            else
                paramName = msg.Name;
            end
            
            % coder assumptions error id for hardware settings
            baseHWCode = 'Connectivity:target:CoderAssumptionsMismatch';
                        
            % Add the parameter name to the basic mismatch exception code
            exceptionCode = [':' msg.Name];
            [mismatchMsg, hwSettingException] = rtw.pil.SILPILInterface.getCAMismatchMessageAndMExcepion(...
                    paramName, msg.Expected, msg.Actual, msg.Message, exceptionCode);
            
            switch msg.Status
                case 'Fail'                   
                    % add the new generic mismatch exception to the
                    % previous hardware settings exception
                    if isempty(previousExc)
                        baseHWMsg = message(baseHWCode, hyperlink, '');
                        previousExc = MException(baseHWCode, '%s', baseHWMsg.getString);
                    end
                    newExc = addCause(previousExc, hwSettingException);
                case 'Warning'
                    % Only display a warning
                    % The warning's code is the same as the base
                    % exception, but the text also includes the coder
                    % assumption message information.
                    
                    % Trailing space gets trimmed by getString(), so
                    % add it again.
                    warnMsg = message(baseHWCode, hyperlink,...
                        [mismatchMsg.getString ' ']);
                    warning(baseHWCode, '%s', warnMsg.getString);
                otherwise
                    assert(false, 'Unexpected status: %s', msg.Status)
            end
        end
        
        function exception = coderAssumptionFTZ(msg, componentName, isSLConfig)
            % return an mException obj caused by a mismatch in the
            % simulation behavior of denormal numbers
            
            % There is no relevant option in MATLAB Coder. Only
            % process this message in the case of Simulink Coder.
            exception = MException.empty;
            if isSLConfig && strcmp(msg.Status, 'Fail')
                % map the on/off message values to the parameter values
                xformMap = containers.Map({'on','off'}, {'FlushToZero', 'GradualUnderflow'});
                % create mismatch setting exception
                parameterName = 'DenormalBehavior';
                exceptionSubCode = '';
                [~, ftzException] = rtw.pil.SILPILInterface.getCAMismatchMessageAndMExcepion(...
                    parameterName, xformMap(msg.Expected), xformMap(msg.Actual), msg.Message, exceptionSubCode);

                % create the higher level floating point mismatch exception
                command = @()configset.highlightParameter(componentName, parameterName);
                hyperlink = targets_hyperlink_manager('new', ...
                    parameterName, command);
                floatMismatchCode = 'Connectivity:target:CoderAssumptionsMismatchFloatNumbers';
                floatMismatchMsg = message(floatMismatchCode, hyperlink);
                mExcFloat = MException(floatMismatchCode, floatMismatchMsg.getString);
                % populate the floating point numbers mismatch cause
                exception = mExcFloat.addCause(ftzException);
            end
        end
        
        function [msg, except] = getCAMismatchMessageAndMExcepion(...
                paramName, expected, actual, messageStr, excCode)
            % get message and MException for a coder assumptions mismatch
            
            msgCode =  'Connectivity:target:CoderAssumptionsMismatchSetting';
            % append the exception code to the message code
            excCode = [msgCode excCode] ;
            % lookup mismatch message and create MException
            msg = message(msgCode, paramName, expected, actual, messageStr);
            except = MException(excCode, msg.getString);
        end
               
        function [description, name] = getParameterInfo(model, parameter)
            cs = getActiveConfigSet(model);
            info = configset.getParameterInfo(cs, parameter);
            description = info.Description;
            assert(~isempty(description), 'parameter description should not be empty');
            name = info.Name;
        end
    end
    
    methods(Static, Access = 'public')
        
        function [changedMsg] = fixRemoveZeroInitSettings(model, settingsToChange)
            % "settingsToChange" is a comma-separated list of settings to
            % be turned to 'on' in order to silence the MemoryAtStartup
            % warning.

            settingsToChange = strsplit(settingsToChange, ', ');
            changedMsg = cell(size(settingsToChange));
            
            for i=1:length(settingsToChange)
                changedMsg{i} = configset.internal.fixIt(model, settingsToChange{i}, 'on');
            end
            
            changedMsg = strjoin(changedMsg, '\n');
        end
        
        function coderAssumptionsCallback(messages, hyperlink, model, ...
                silpilInterfaceStr, inTheLoopType, blockPath, isSLConfig)
            % Function called when coder assumption verification is
            % complete. Messages is a struct array. Hyperlink is a link to
            % the configuration page. isSLConfig is a boolean flag.
            
            coder.assumptions.CoderAssumptions.emitReport(...
                messages, model, silpilInterfaceStr, inTheLoopType, blockPath);
            
            % build list of failure messages
            hardwareSettingsExc = MException.empty;
            denormalSettingsExc = MException.empty;
            
            % Group the global and dynamic memory messages together and
            % remove them from the messages list
            [messages, memoryMsg] = rtw.pil.SILPILInterface.extractMessages(messages, 'MemoryAtStartup');
            
            % Off-load the memory tests to a different function
            rtw.pil.SILPILInterface.coderAssumptionMemoryAtStartup( ...
                                   memoryMsg, model, isSLConfig, silpilInterfaceStr, inTheLoopType, blockPath);
                               
            % Read the messages and create cause exceptions and warnings
            for i = 1:numel(messages)
                if ismember(messages(i).Status, {'Fail', 'Warning'})
                    switch messages(i).Name  
                        case 'DenormalFlushToZero'
                            % Get an exception caused by denormal behavior mismatch
                            denormalSettingsExc = rtw.pil.SILPILInterface.coderAssumptionFTZ( ...
                                messages(i), model, isSLConfig);

                        otherwise
                            % Get an exception caused by hardware settings mismatches                        
                            hardwareSettingsExc = rtw.pil.SILPILInterface.coderAssumptionHWSettings( ...
                                messages(i), model, isSLConfig, hyperlink, hardwareSettingsExc);
                    end
                end
            end
            
            causeExceptions = [hardwareSettingsExc, denormalSettingsExc];
            
            % throw exception if there were any failures
            if ~isempty(causeExceptions)
                baseCode = 'Connectivity:target:CoderAssumptionsMismatchGeneral';
                baseMsg = message(baseCode);
                baseException = MException(baseCode, baseMsg.getString);
                for causeIdx = 1:length(causeExceptions)
                   baseException = addCause(baseException, causeExceptions(causeIdx)); 
                end
                throwAsCaller(baseException);
            end
        end        
    end
            
    methods (Access = 'private')                                                    
        function addTimerSources(this, addSources, buildInfo)
            
            service = this.getSILPILWrapperUtils.getTargetServices().getProfilingService();

            timer = service.getTimer();
            
            coder.internal.buildInfoTimerUpdate...
                (buildInfo, timer, {}, {}, addSources);
        end
        
        function addCodeInstrAppServiceSources(~, addSources, buildInfo)
            
            xilSrcPath = coder.connectivity.PILApplicationDependencies.PILPath;
            
            codeInstrDefineName = 'CODE_INSTRUMENTATION_ENABLED';
            srcFiles = {'codeinstr_data_stream.c'
                        'codeinstr_data_stream.h'};
            isHeader = [false
                        true];            
                        
            codeInstrTgtAppSvcIncludeDir = fullfile(matlabroot, 'extern', 'include',...
                'coder','connectivity','CodeInstrTgtAppSvc');
            buildInfo.addIncludePaths(codeInstrTgtAppSvcIncludeDir);
            buildInfo.addIncludeFiles('CodeInstrTgtAppSvc.h', codeInstrTgtAppSvcIncludeDir);
            buildInfo.addIncludeFiles('CodeInstrTgtAppSvc_CInterface.h', codeInstrTgtAppSvcIncludeDir);

            if rtw.connectivity.Utils.isCommServiceAPIEnabled
                if ispc
                    libFolder = fullfile(matlabroot, 'extern','lib', ...
                        computer('arch'), 'microsoft');
                    ext = '.lib';                    
                else                    
                    libFolder = fullfile(matlabroot, 'bin', computer('arch'));
                    ext=system_dependent('GetSharedLibExt');
                end                              
                                
                profileLib = {fullfile(libFolder, ['libmwcoder_CodeInstrTgtAppSvc' ext])};
                rtw.pil.RtIOStreamApplicationFramework.addLibsToBuildInfo(buildInfo, profileLib);
            else
                srcFiles = [srcFiles;'codeinstr_rtiostream.c'];
                isHeader = [isHeader;false];
            end
            
            buildInfo.deleteDefines(codeInstrDefineName);
            if addSources
                for i=1:length(srcFiles)
                    srcFile = srcFiles{i};
                    if isHeader(i)
                        buildInfo.addIncludeFiles(srcFile, xilSrcPath);
                    else
                        buildInfo.addSourceFiles(srcFile, xilSrcPath);
                    end
                end
                buildInfo.addDefines([codeInstrDefineName '=1'], 'OPTS');
                buildInfo.addSourcePaths(xilSrcPath);
                buildInfo.addIncludePaths(xilSrcPath);                
            else
                numSrc = length(buildInfo.Src.Files);
                srcFileNames = cell(numSrc,1);
                for i=1:numSrc
                    srcFileNames{i} = buildInfo.Src.Files(i).FileName;
                end
                numHeader = length(buildInfo.Inc.Files);
                headerFileNames = cell(numHeader,1);
                for i=1:numHeader
                    headerFileNames{i} = buildInfo.Inc.Files(i).FileName;
                end
                [~, ~, incRemoveIdx] = intersect(srcFiles, headerFileNames);
                [~, ~, srcRemoveIdx] = intersect(srcFiles, srcFileNames);
                incKeepIdx = true(size(headerFileNames));
                srcKeepIdx = true(size(srcFileNames));
                incKeepIdx(incRemoveIdx) = false;
                srcKeepIdx(srcRemoveIdx) = false;
                buildInfo.Src.Files = buildInfo.Src.Files(srcKeepIdx);
                buildInfo.Inc.Files = buildInfo.Inc.Files(incKeepIdx);                                
            end
        end
        
        function wrapperInfo = getWrapperInfo(this)                        
            appDir = this.getApplicationDir;
            wrapperInfo_mat = fullfile(appDir, 'wrapperInfo.mat');                            
            loadWrapper = false;
            if ~exist(wrapperInfo_mat, 'file')
                % flag missing wrapperInfo
                rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorWrapperIsIncomplete');
            end                        
            if isempty(this.WrapperInfo)
                loadWrapper = true;    
            else
               % see if cache is out of date with respect to MAT file     
               d = dir(wrapperInfo_mat);
               if d.datenum > this.WrapperInfo.FileTimestamp
                   loadWrapper = true;
               end
            end
            if loadWrapper                       
                % load wrapperInfo
                wrapperInfoData = load(wrapperInfo_mat);
                % version check
                rtw.pil.SILPILInterface.validateWrapperVersion(wrapperInfoData);
                % update cache
                this.WrapperInfo.Data = wrapperInfoData;
                d = dir(wrapperInfo_mat);
                this.WrapperInfo.FileTimestamp = d.datenum;
            end    
            wrapperInfo = this.WrapperInfo.Data;
        end                    

        function validateStringArg(this, arg)  %#ok<INUSL>
            % basic validation for string arguments
            rtw.connectivity.Utils.validateArg(arg, ...
                                               'char');
            assert(~isempty(arg), ...
                   'String argument must not be empty.');            
        end                                
        
        function timestamp = getTimestamp(this, file)
            % determine current timestamp            
            switch file
                case 'CodeInfo'  
                    filepath = this.getCodeInfoPath;
                case 'XrelSourceFile'
                    filepath = this.XRelSourceFile;
                case 'BuildInfo'
                    filepath = this.getBuildInfoPath;
                otherwise
                    assert(false, 'Unexpected argument');
            end
            dirStruct = dir(filepath);
            assert(~isempty(dirStruct),...
                '%s must exist to query timestamp.', filepath);
            timestamp = dirStruct.datenum;
        end                       
        
        function startApplication(this, rtioStreamApplictionService, topContext)
            
            exeStr = this.ApplicationExecutable;
            if isempty(exeStr)
                exeStr = rtw.pil.ApplicationBuildManager.calculateApplicationExecutablePath( ...
                                this.SILPILWrapperUtils.getComponentArgs(), ...
                                this.getTargetServices());
            end
 
            if ~this.isSIL
                currDir = pwd;
                if strncmp(currDir, exeStr, length(currDir))
                    % Display relative path only
                    exeStr=exeStr(length(currDir)+2:end);
                end
                startMessage = message('PIL:pil:PILStartApplication', ...
                    exeStr);
                disp(startMessage.getString);
                
                if this.InTheLoopType == rtw.pil.InTheLoopType.MATLABFcn
                    %In addition for MATLAB Coder we can print an message
                    %explaining how to terminate the execution.
                    clearCommand = sprintf('clear %s', this.getPILSfunction());
                    
                    terminateMessage = message('PIL:pil:MATLABCoderTerminateMessage', ...
                        targets_hyperlink_manager('new', clearCommand, clearCommand));
                    fprintf('    %s\n', terminateMessage.getString);
                end
            end
            
            SILDebuggingEnabled = this.getSILPILWrapperUtils.SILDebuggingInfo.IsEnabled;
            if this.isSIL && SILDebuggingEnabled                
                debuggingInfo = this.getSILPILWrapperUtils.SILDebuggingInfo;                
                 rtioStreamApplictionService.start(exeStr, ...
                                                   debuggingInfo, ...
                                                   topContext);
            else
                 rtioStreamApplictionService.start(exeStr);
            end
        end
        
        % check if the configReg of this object matches the supplied
        % configName and configClass
        function isMatchingConfigReg = isMatchingConfig(this, targetServicesChecksum)
            isMatchingConfigReg = ...
                isequal(targetServicesChecksum, this.SILPILWrapperUtils.getTargetServices().getServiceInstanceIdentifier());
        end                                                  
        
        function generateHostSfunction(this, lDefaultMexCompilerKey)
            % GENERATEHOSTSFUNCTION - Generate host side PIL interface
            
            % get PIL sfun paths
            [~, sfunSourcePath, sfunBinDir] = this.getPILSfunction;
                        
            sf = this.CodeInterfaceFactory.createHostSfunction(this.SILPILWrapperUtils, ...
                                                          sfunSourcePath, ...
                                                          sfunBinDir);                                    
            % create and build the s-function
            build(sf, lDefaultMexCompilerKey);                                                   
        end
        
        function generateCoderAssumptions(this, lXilCompInfo)
            % write coderassumptions source files
            configInterface = this.SILPILWrapperUtils.getConfigInterface;                                    
            % componentArgs
            componentArgs = this.SILPILWrapperUtils.getComponentArgs();
            componentBuildInfoPath = fullfile(componentArgs.getComponentCodePath(), 'buildInfo.mat');

            coder.assumptions.CoderAssumptions.generateCoderAssumptionsChecks( ...
                this.CodeDescriptorDir, this.AnchorDir, configInterface, componentArgs, ...
                this.isSIL, componentBuildInfoPath, ...
                this.SILPILWrapperUtils.shouldObfuscateXILWrapperFiles, ...
                lXilCompInfo);
        end
        
        function setMemUnitDefines(this, buildInfo)
            % set the current MEM_UNIT_BYTES & MemUnit_T defines            
            % remove any existing settings
            buildInfo.deleteDefines('MEM_UNIT_BYTES');
            buildInfo.deleteDefines('MemUnit_T');
            memUnitInfo = this.SILPILWrapperUtils.getMemUnitInfo;            
            memUnitType = rtw.connectivity.TypeUtils.builtinToHostCType(memUnitInfo.memUnitType);
            % add new values
            buildInfo.addDefines(['MEM_UNIT_BYTES=' int2str(memUnitInfo.memUnitBytes)], ...
                                  'OPTS');
            buildInfo.addDefines(['MemUnit_T=' memUnitType], 'OPTS');
        end                                                                    
        
        function checkWrapperIsUpToDate(this, buildSFunctionOnly, lXilCompInfo)
            % check host/target wrapper is up to date                                                                                                                        
            wrapperInfo = this.getWrapperInfo;
            
            % make sure any error is re-thrown as an ErrorWrapperOutOfDate error
            try
                % make sure that all wrapper target files exist and have not
                % been modified (either by partial wrapper creation or 
                % manually) since the wrapper creation completed
                [~, ~, ~, ...
                 allDependencies] = this.getApplicationDependencies(buildSFunctionOnly, lXilCompInfo);
                                                                                                                              
                for i=1:length(allDependencies)
                    target = allDependencies{i};                                                                                
                    % check existence on this file
                    if ~exist(target, 'file')
                        rtw.pil.ProductInfo.error('pilverification', ...
                            'ErrorWrapperMissingFile', target);
                    end                                        
                    % compare file timestamps
                    % most recent file has biggest datenum
                    targetStruct = dir(target);
                    %
                    if targetStruct.datenum > wrapperInfo.wrapperTimeStamp
                        % wrapperInfo is out of date
                        rtw.pil.ProductInfo.error('pilverification', ...
                            'ErrorWrapperFileModified', target);
                    end
                end                                                                
                                
                % check configName and configClass
                if ~this.isMatchingConfig(wrapperInfo.targetServicesChecksum)
                    % trigger rebuild
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorWrapperIsIncomplete');
                end
                % check whether to trigger rebuild
                %
                lGlobalRegistry = this.getClientInterface.createExecTimeGlobalReg(this);
                isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
                isCodeInstrumentationEnabled = this.SILPILWrapperUtils.isCodeInstrumentationEnabled;

                triggerRebuild =...
                    (isCodeInstrumentationEnabled &&...
                     ~isempty(lGlobalRegistry) &&...
                     ~isequal(lGlobalRegistry.getModelInstrumentationChecksum,...
                              wrapperInfo.lCodeInstrumentationChecksum)) || ...
                    ~isequal(this.SILPILWrapperUtils.CodeProfilingSaveOptions,...
                             wrapperInfo.profilingSaveOpts) || ...
                    ~(isExecutionProfilingEnabled == wrapperInfo.isExecutionProfilingEnabled);

                if ~triggerRebuild && isExecutionProfilingEnabled
                    triggerRebuild =  ... 
                        ~isequal(lGlobalRegistry.getFeatureChecksum('GranularityOn'), ...
                                 wrapperInfo.lProfilingGranularityOnChecksum) || ...
                        ~isequal(lGlobalRegistry.getFeatureChecksum('GranularityOff'), ...
                                 wrapperInfo.lProfilingGranularityOffChecksum);
                end

                if ~triggerRebuild
                    % In Simulink Coverage, we use a "special" probe that is inserted in the SIL/PIL
                    % wrapper code itself (after each step and on terminate) so we have to trigger a
                    % rebuild of the wrapper when Simulink Coverage is disabled.
                    wasSlCovEnabled = isfield(wrapperInfo, 'SlCovEnabled') && wrapperInfo.SlCovEnabled;
                    if this.getClientInterface().isMWCodeCoverageEnabled()
                        triggerRebuild = (wasSlCovEnabled ~= this.SILPILWrapperUtils.isSlCovEnabled());
                    else
                        triggerRebuild = wasSlCovEnabled;
                    end
                end
                
                if triggerRebuild
                    % trigger rebuild
                    rtw.pil.ProductInfo.error('pilverification', ...
                                              'ErrorWrapperIsIncomplete');
                end
                
                if isExecutionProfilingEnabled
                    % Restore the task profiling registry
                    this.SILPILWrapperUtils.setProfilingTaskRegistry(...
                        wrapperInfo.lProfilingTaskRegistry);
                    this.SILPILWrapperUtils.setProfilingOverheadRegistry(...
                        wrapperInfo.lProfilingOverheadRegistry);
                    this.SILPILWrapperUtils.setProfilingCaptureRegistry(...
                        wrapperInfo.lProfilinglCaptureRegistry);
                end

                % check loggingData checksum
                loggingData = this.SILPILWrapperUtils.getLoggingData;
                if isempty(loggingData)
                    loggingChecksum = [];
                else
                    loggingChecksum = loggingData.getChecksum;
                end
                if ~isequal(loggingChecksum, wrapperInfo.loggingChecksum)
                    % trigger rebuild
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorWrapperIsIncomplete'); 
                end                    
                if wrapperInfo.TopLevelSfunctionType ~= this.getTopLevelSfunctionType()
                    % trigger rebuild
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorWrapperIsIncomplete'); 
                end      
            catch origE
                % construct standard error with origE as cause
                [newId, newMessage] = rtw.pil.ProductInfo.message('pilverification', 'ErrorWrapperOutOfDate');
                newException = MException(newId, ...
                    newMessage);
                newException = newException.addCause(origE);
                throw(newException);                
            end
        end                
        
        function checkApplicationIsUpToDate(~, pilExe, wrapperTimeStamp)
            % CHECKAPPLICATIONISUPTODATE - Check to see if the PIL Application is up
            %                              to date relative to the PIL Wrapper
            
            % we assume wrapper is up to date (i.e. we assume that buildApplication is
            % always called after buildWrapper)
                % check existence
                if ~exist(pilExe, 'file')
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorApplicationMissing', pilExe);
                end                                                
                pilExeStruct = dir(pilExe);
                % compare timestamps                
                if wrapperTimeStamp > pilExeStruct.datenum
                    % dependencies are more recent
                    % PIL Application is out of date
                    rtw.pil.ProductInfo.error('pilverification', ...
                        'ErrorApplicationOlderThanWrapper');
                end
        end                
        
        function slbuild_profile(this, desc, startOrEnd)             
            this.ClientInterface.perfProfile([mfilename('class') ': ' desc], ...
                                             startOrEnd, ...
                                             this.IsProfilingEnabled, ...
                                             this.ProfilingMdlRefTgtType, ...
                                             this.getSILPILWrapperUtils.getLoadableRootComponent);           
        end                
        
        function initWrapperSourceInfo(this)            
            % get the Application Target Src Dir
            if this.isXRelBlock && this.UsingResourcesDir
                % use buildInfo.mat in the relocatable _resources folder
                % rather that the original xil_interface.c
                subfolderName = this.getXilSubDirName;
                [appDir, sourceFilename, sourceExt] = fileparts(...
                    fullfile(this.getCodeDir,subfolderName,'buildInfo.mat'));
            else
                appDir = this.getApplicationDir;
                targetLang = this.SILPILWrapperUtils.getParam('TargetLang');
                [sourceFilename, sourceExt] = ...
                    rtw.pil.SILPILInterface.getWrapperSourceFilenameAndExt(targetLang);
            end
            
            wrapperSourceInfo.Name = [sourceFilename sourceExt];
            wrapperSourceInfo.Path = appDir;
            this.WrapperSourceInfo = wrapperSourceInfo;
        end
        
        function subDirName = getXilSubDirName(this)
            if this.isSIL
                subDirName = rtw.connectivity.Utils.getSilSubDir;
            else
                subDirName = rtw.connectivity.Utils.getPilSubDir;
            end
        end
        
        function wrapperInfo = getWrapperInfoFileName(this)
            % return the full path to the wrapperInfo file
            appDir = this.getApplicationDir;
            wrapperInfo = fullfile(appDir, 'wrapperInfo.mat');
        end
        
        function type = getTopLevelSfunctionType(this)
            % return a different ID depending on the current Sfunction type
            if this.isTopModelXILSim()
                % top-level simulation via ModelBlock
                type = rtw.pil.XILTopSfunctionType.TopLevel;
            elseif this.InTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone
                % ModelBlock 'Top model' code interface
                type = rtw.pil.XILTopSfunctionType.ModelBlockTop;
            elseif this.InTheLoopType == rtw.pil.InTheLoopType.Block
                % PILBlock
                type = rtw.pil.XILTopSfunctionType.Block;
            else
                % any other sim type that does not share the top-level
                % folder
                type = rtw.pil.XILTopSfunctionType.OtherNonSharingTopLevel;
            end
        end
    end
    
    methods(Access = protected)
        function cpObj = copyElement(this)
              % create a copy of this object
              cpObj = copyElement@matlab.mixin.Copyable(this);
              % recursively call copy on SILPILWrapperUtils, needs to be
              % called explicitly because it is an handle.
              % we need to copy SILPILWrapperUtils in order to copy 
              % CodeInfoUtils and disconnect from CodeDescriptor
              cpObj.SILPILWrapperUtils = this.SILPILWrapperUtils.copy;
              % replace the SILPILWrapperUtils reference to SILPILInterface
              % with a reference to the new copy of SILPILInterface
              cpObj.SILPILWrapperUtils.updateSILPILInterface(cpObj);
        end
    end
    
    methods (Access = private, Static)
        function [messages, extractedMsg] = extractMessages(messages, stringToSearch)
            % Extract messages with Name containing a certain string
            messagesNames = {messages.Name};
            msgIndices= contains(messagesNames, stringToSearch);
            extractedMsg = messages(msgIndices);
            messages = messages(~msgIndices);
        end
    end
end

% LocalWords:  Binfo pil binfo sfun psf pbs pilverification Sfunction Datatype
% LocalWords:  datatype cosimulation GETPILVERSION LOADCODEINFO helptargets
% LocalWords:  SFUNCTIONGATEWAYINITIALIZE SFUNCTIONGATEWAYGETRTIOSTREAMINFO
% LocalWords:  SFUNCTIONGATEWAY proj modelname tmf rtwinfomatman rtwinfomat
% LocalWords:  mdlref tmwinternal tgt GETAPPLICATIONDEPENDENCIES sfunction
% LocalWords:  GENERATEHOSTSFUNCTION UDATA
% LocalWords:  GENERATETARGETINTERFACE modelRefUtil checkslprjdir binfo's
% LocalWords:  CHECKAPPLICATIONISUPTODATE TOI
