classdef (Hidden = true) SimulinkInterface < coder.connectivity.ClientInterface
%CONNECTIVITYSIMULINKCLIENT Implements ClientInterface for Simulink /
%SimulinkCoder.
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.
    
%   Copyright 2013-2021 The MathWorks, Inc.
    properties (Access = private)
        CoreSimulinkInterface coder.connectivity.CoreSimulinkInterface = coder.connectivity.CoreSimulinkInterface.empty; 
    end
    
    methods                 
        function topContext = getTopContext(~, blockPath, silPILInterface) 
            if strcmp(silPILInterface.getInTheLoopType, 'Block')
                topContext = strtok(blockPath, '/');
            else
                topContext = silPILInterface.getSILPILWrapperUtils.getInstrumentationSettingsModel;
            end
        end
        
        function closeNotification(~, context, notificationID)
            editor = GLUE2.Util.findAllEditors(context);
            if isempty(editor)
                return
            end
            
            editor.closeNotificationByMsgID(notificationID);
        end
        
        function deliverNotification(~, context, notificationID, notificationMsg)
            editor = GLUE2.Util.findAllEditors(context);
            if isempty(editor)
                return
            end                        
            editor.deliverInfoNotification(notificationID, notificationMsg);
        end
                
        function config = createConfigInterface(~, config, codeGenComponent)             
            config = coder.connectivity.SimulinkConfig(config, codeGenComponent);
        end
            
        function config = createConfigInterfaceFromInfoStruct(~, infoStruct, codeGenComponent)
            config = coder.connectivity.SimulinkCoderConfig(infoStruct, codeGenComponent);
        end
        
        function codeInterfaceFactory = createCodeInterfaceFactory(~, silPILInterface, config)
            % create CodeInterfaceFactory            
            % check for AUTOSAR based on the config set
            isAUTOSAR = strcmp(config.getParam('AutosarCompliant'), 'on');
            inTheLoopType = silPILInterface.getInTheLoopType();
            if coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(silPILInterface.getTopModelForModelBlockPIL)
                codeInterfaceFactory = rtw.pil.AtomicSubsystemCodeInterfaceFactory(...
                    isAUTOSAR && (inTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone));
            else
                if inTheLoopType == rtw.pil.InTheLoopType.ModelBlock                
                    codeInterfaceFactory = rtw.pil.ModelRefCodeInterfaceFactory();
                elseif inTheLoopType == rtw.pil.InTheLoopType.RLSSubsystem
                    codeInterfaceFactory = rtw.pil.RLSCodeInterfaceFactory();
                else
                    codeInterfaceFactory = rtw.pil.StandaloneCodeInterfaceFactory(isAUTOSAR);
                end                                        
            end
        end
        
        function fcnStr = getSILPILInterfaceFcnStr(~)
            % return function handle to static method encoded as a string
            fcnStr = '@coder.connectivity.SimulinkInterface.getSILPILInterface';
        end
        
        function rootLoggingPath = initializeHookBody(~, topModel, blockPath, inTheLoopType)  
        
            switch inTheLoopType
                case {rtw.pil.InTheLoopType.ModelBlock, ...
                      rtw.pil.InTheLoopType.ModelBlockStandalone, ...
                      rtw.pil.InTheLoopType.RLSSubsystem}
                    % root logging path is handled directly in the s-function
                    rootLoggingPath = '';                
                case rtw.pil.InTheLoopType.Block
                    % dynamically determine SIL/PIL block context (note that
                    % the s-function itself is not dependent on whether we're
                    % doing Top-Model vs Block based SIL/PIL)
                    isTopModelPILContext = slfeature('XilNoWrapper');
                    if isTopModelPILContext
                        % for Top-Model SIL/PIL, we assume the logging elements
                        % associated with the SIL/PIL block are the only
                        % instances.   Therefore, we don't need to prefix with
                        % a rootLoggingPath
                        rootLoggingPath = '';
                    else
                        % for SIL/PIL block the logging elements associated
                        % with the block are not unique; there may be other
                        % block instances or the original component may be
                        % present.
                        % Therefore, prefix logging paths with SIL/PIL block path
                        % use ':' to indicate this is special block that
                        % represents a whole hierarchy beneath it.
                        rootLoggingPath = [blockPath ':'];
                    end                    
                otherwise
                    assert(false, 'Unknown inTheLoopType: %d', inTheLoopType);
            end

            % report an info message about data import/export save to 
            % workspace options if needed
            loadInitialState = strcmp(get_param(topModel, 'LoadInitialState'), 'on');
            saveFinalState = strcmp(get_param(topModel, 'SaveFinalState'), 'on');
            if loadInitialState || saveFinalState
                if coder.connectivity.XILSubsystemUtils.isSubsystemWorkflow(topModel)
                    harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(topModel);
                    blockPath = Simulink.harness.internal.getActiveHarnessCUT(harnessInfo.model);
                end
                [~, message] = rtw.pil.ProductInfo.message('pilverification', ...
                    'StateImportExportWarning', ...
                    topModel, ...
                    blockPath);
                if loadInitialState
                    [~, message] = rtw.pil.ProductInfo.message('pilverification', ...
                        'InitialStateWarning', ...
                        message);
                end
                sldiagviewer.reportInfo(message);
            end
        end
        
        function executionCodeCoverageCallback(~, ~, ~, ~, ~, ~)
            assert(false, 'This callback is not called in this workflow');            
        end
        
        % callback for stack profiling
        function stackProfileCallback(~, silPILInterface, blockPath, profData, rootModel, inTheLoopType)
            wrapperUtils = silPILInterface.getSILPILWrapperUtils();
            isStackProfilingEnabled = wrapperUtils.isStackProfilingEnabled();
            
            if isStackProfilingEnabled
                workspaceVarName = wrapperUtils.CodeStackProfileVariable;
                codeInstrInfo = silPILInterface.getSILPILWrapperUtils().getComponentArgs().getCodeInstrInfo();
                if isempty(codeInstrInfo)
                    % protected models do not use the "instrumented"
                    % folder, so codeInstrInfo will be empty in this case.
                    assert(silPILInterface.isModelBlockXIL &&...
                        strcmp(get_param(blockPath, 'ProtectedModel'), 'on'),...
                        'codeInstrInfo should only be empty for protected models')
                end
                originalComponentName = silPILInterface.getFullComponentPath;
                if silPILInterface.isTopModelXILSim
                    % Reference original component instead of wrapper model
                    lSimulationComponent = originalComponentName;
                    lTopModel = originalComponentName;
                else
                    % Simulation component is derived in the same way for both Model block case and
                    % the XIL block cases. The getBlockPath method returns a
                    % cell array of block paths where the final element
                    % identifies the path to the XIL component. When this
                    % component is inside a referenced model, the previous
                    % elements from getBlockPath identify the chain of block
                    % paths through the model reference hierarchy.
                    lBlockPaths = getBlockPath(silPILInterface);
                    
                    lTopModel = rootModel;
                    
                    % If the XIL component is inside a model reference hierarchy, only keep the path
                    % for the bottommost model and discard the others.
                    lSimulationComponent = lBlockPaths{end};
                    lSimulationComponentAncestors = lBlockPaths(1:end-1);
                    for i = 1:length(lSimulationComponentAncestors)
                        lSimulationComponentAncestors{i} = Simulink.ID.getSID(lSimulationComponentAncestors{i});
                    end
                end
                lCreateWorkspaceVariable = strcmp(get_param(rootModel, 'ReturnWorkspaceOutputs'), 'off');
                
                lReportFcn = 'coder.internal.SLCExecTimeReport';
                
                mainDir = silPILInterface.getApplicationDir;
                codeDir = silPILInterface.getCodeDir;
                lGlobalRegistry = coder.profile.CoderInstrumentationInfo.getGlobalRegistry(mainDir, true);
                if isempty(lGlobalRegistry)
                    return;
                end
                lGlobalRegistry.updateAnchorFolder(codeDir);
                
                profVar = coder.profile.ExecutionStack(profData, lReportFcn);
                profVar.GlobalRegistry = lGlobalRegistry;
                profVar.CodeDir = codeDir;
                profVar.TargetWordSizeInBits = wrapperUtils.getParam('TargetWordSize');
                profVar.TargetLargestAtomicIntegerType = ...
                    wrapperUtils.getParam('TargetLargestAtomicInteger');
                profVar.TopMostComponentName = lTopModel;
                profVar.OriginalComponentName = lSimulationComponent;
                profVar.ShowCodeGenReportLink = false;
                
                profVar.traceabilityUpdate();
                
                lCreatedVar = false;
                if lCreateWorkspaceVariable
                    if ~evalin('base', ['exist(''' workspaceVarName ''',''var'')==1'])
                        assignin('base', workspaceVarName, profVar);
                        lCreatedVar = true;
                    end
                end
                if lCreateWorkspaceVariable
                    % First check that the variable does not currently exist
                    % (it should have been deleted in the start callback)
                    if ~lCreatedVar
                        rtw.pil.ProductInfo.error('pilverification', ...
                            'ExecutionProfileNameClash', ...
                            workspaceVarName, blockPath);
                    end
                else
                    % register (if first time) and assign variable in the 
                    % single output object
                    coder.connectivity.SimulinkInterface.addVarToSingleOutput ...
                        (inTheLoopType, rootModel, blockPath, originalComponentName, ...
                        workspaceVarName, ...
                        profVar);
                end
                
                profVar.report();
            end
        end
        
        % callback for stack profiling
        function stackProfileStartCallback(~, silPILInterface, ~, rootModel, ~)
            wrapperUtils = silPILInterface.getSILPILWrapperUtils;
            isExecutionProfilingEnabled = wrapperUtils.isStackProfilingEnabled();
            if isExecutionProfilingEnabled
                set_param(rootModel, 'IsCodeExecutionProfilingActive', 'on');
                workspaceVarName = wrapperUtils.CodeStackProfileVariable;
                if strcmp(get_param(rootModel, 'ReturnWorkspaceOutputs'), 'off')
                    % Clear the workspace variable
                    evalin('base',['clear ' workspaceVarName]);
                end
            end
        end

        function executionProfileCallback(~, silPILInterface, blockPath, profData, rootModel, inTheLoopType)
            wrapperUtils = silPILInterface.getSILPILWrapperUtils;
            isExecutionProfilingEnabled = wrapperUtils.isProfilingEnabled;
            
            if isExecutionProfilingEnabled
                workspaceVarName = wrapperUtils.CodeExecutionProfileVariable;
                codeInstrInfo = silPILInterface.getSILPILWrapperUtils()...
                    .getComponentArgs().getCodeInstrInfo();
                if isempty(codeInstrInfo)
                    % protected models do not use the "instrumented"
                    % folder, so codeInstrInfo will be empty in this case.
                    assert(silPILInterface.isModelBlockXIL &&...
                        strcmp(get_param(blockPath, 'ProtectedModel'), 'on'),...
                        'codeInstrInfo should only be empty for protected models')
                    lInstrCodeSubFolder = '';
                else
                    lInstrCodeSubFolder = codeInstrInfo.getInstrSrcFolder;
                end

                originalComponentName = silPILInterface.getFullComponentPath;
                isAtomicSubsystemSimulation = coder.connectivity.XILSubsystemUtils.isAtomicSubsystem(rootModel);
                if silPILInterface.isTopModelXILSim
                    % Reference original component instead of wrapper model
                    lSimulationComponent = originalComponentName;
                    lSimulationComponentAncestors = [];
                elseif isAtomicSubsystemSimulation
                    % Reference the original subsystem instead of the model
                    % block referencing the hidden model
                    harnessInfo = Simulink.harness.internal.getHarnessInfoForHarnessBD(rootModel);
                    lSimulationComponent = Simulink.harness.internal.getActiveHarnessCUT(harnessInfo.model);
                    lSimulationComponentAncestors = [];
                else
                    % Simulation component is derived in the same way for both Model block case and
                    % the XIL block cases. The getBlockPath method returns a
                    % cell array of block paths where the final element
                    % identifies the path to the XIL component. When this
                    % component is inside a referenced model, the previous
                    % elements from getBlockPath identify the chain of block
                    % paths through the model reference hierarchy.
                    lBlockPaths = getBlockPath(silPILInterface);
                    
                    % If the XIL component is inside a model reference hierarchy, only keep the path
                    % for the bottommost model and discard the others. Potential
                    % enhancement: g1414924.
                    lSimulationComponent = lBlockPaths{end};
                    lSimulationComponentAncestors = lBlockPaths(1:end-1);
                    for i = 1:length(lSimulationComponentAncestors)
                        lSimulationComponentAncestors{i} = Simulink.ID.getSID(lSimulationComponentAncestors{i});
                    end
                end

                lCreateWorkspaceVariable = strcmp(get_param(rootModel, 'ReturnWorkspaceOutputs'), 'off');
                lTimerTicksPerSecond = wrapperUtils.getProfilingTimer.getTicksPerSecond;
                lHWCounterUnit = wrapperUtils.ProfilingHWCounterUnit;
                
                % Update traceability data (code descriptor must be up to date for all models in
                % the hierarchy)
                lUpdateTraceability = ~wrapperUtils.isModelProtected;
                lSaveOptions = wrapperUtils.CodeProfilingSaveOptions;

                [profVar, lCreatedVar] = coder.profile.finalizeProfiling(wrapperUtils.getInstrumentationSettingsModel, ...
                                                   originalComponentName, ...
                                                   workspaceVarName, profData, ...
                                                   silPILInterface.getApplicationDir, ...
                                                   silPILInterface.getCodeDir, ...
                                                   lTimerTicksPerSecond, ....
                                                   lHWCounterUnit, ...
                                                   lInstrCodeSubFolder, ...
                                                   lSimulationComponent, ...
                                                   lSimulationComponentAncestors, ...
                                                   lUpdateTraceability, ...
                                                   lCreateWorkspaceVariable, ...
                                                   lSaveOptions, ...
                                                   isAtomicSubsystemSimulation);

                if lCreateWorkspaceVariable
                    % First check that the variable does not currently exist
                    % (it should have been deleted in the start callback)
                    if ~lCreatedVar
                        rtw.pil.ProductInfo.error('pilverification', ...
                            'ExecutionProfileNameClash', ...
                            workspaceVarName, blockPath);
                    end
                else
                    % register (if first time) and assign variable in the 
                    % single output object
                    coder.connectivity.SimulinkInterface.addVarToSingleOutput ...
                        (inTheLoopType, rootModel, blockPath, originalComponentName, ...
                        workspaceVarName, ...
                        profVar);
                end
                
            end                        
        end
        
        function executionProfileStartCallback(~, silPILInterface, ~, rootModel, ~)
            wrapperUtils = silPILInterface.getSILPILWrapperUtils;
            isExecutionProfilingEnabled = wrapperUtils.isTimeProfilingEnabled();

            if isExecutionProfilingEnabled
                
                set_param(rootModel, 'IsCodeExecutionProfilingActive', 'on');
                
                workspaceVarName = wrapperUtils.CodeExecutionProfileVariable;

                if strcmp(get_param(rootModel, 'ReturnWorkspaceOutputs'), 'off')
                    % Clear the workspace variable
                    evalin('base',['clear ' workspaceVarName]);
                end
            end
        end                                    
        
        function blockHandle = getBlockHandle(this, blockPath)
            %Delegate work to static method
            blockHandle = this.getResolvedBlockHandle(blockPath);            
        end
                                        
        function varargout = dispatchBuildHook(~, componentArgs, hookName, hookObj)        
            rootModel = componentArgs.getModelName;    
            varargout = {[], []};
            
            % If the application and/or component compile stage was up to
            % date the entry hook may not already have been called
            callEntryMethod = any(strcmp(hookName, ...
                {'before_buildApplication', 'before_on_target_execution'}));
            
            if callEntryMethod

                lTopModel = componentArgs.getTopModel;
                lBuildHooks = componentArgs.getBuildHooks;
                lIsSil = componentArgs.isSIL;
                lIsPil = ~lIsSil;
                lIsSilAndPws = componentArgs.isSILAndPWS;
                lBuildHookHandles = coder.coverage.createHooks...
                    (rootModel, lBuildHooks, lTopModel);
                
                % BuildInfo objects will be loaded by the build hook if it needs them
                buildInfo = [];
                lBuildInfoInstr = [];
                lBuildFolderBackwardsCompatibility = '';
                
                lCodeInstrInfo = getCodeInstrInfo(componentArgs);
                if isempty(lCodeInstrInfo)
                    lInstrumentedObjectCodeFolder = '';
                else
                    lInstrumentedObjectCodeFolder = ...
                        getInstrObjFolder(lCodeInstrInfo);
                end   
            
                coder.coverage.BuildHook.initializeCommonHookData...
                    (lBuildHookHandles, ...
                     componentArgs.getConfigInterface.getConfig,...
                     lIsSilAndPws, lIsSil, lIsPil,...
                     @Simulink.output.info, ...
                     @Simulink.output.warning); %Marking for migration to MSLDiagnostic
                
                coder.coverage.BuildHook.initializeHookData...
                    (lBuildHookHandles, ...
                     componentArgs.getAnchorFolder, ...
                     componentArgs.getComponentCodePath, ...
                     lInstrumentedObjectCodeFolder, ...
                     buildInfo, ...
                     lBuildInfoInstr, ...
                     lBuildFolderBackwardsCompatibility);
                
                
                % Simulink coverage needs to know InTheLoopType because it
                % is used to determine the database filename.
                for i=1:length(lBuildHookHandles)
                    lBuildHookHandles{i}.InTheLoopTypeForCoverage = getInTheLoopType(componentArgs);
                end
                
                componentArgs.setBuildHookHandles(lBuildHookHandles);

                coder.coverage.BuildHook.dispatch_entry(lBuildHookHandles);
            end
            
            lBuildHookHandles = componentArgs.getBuildHookHandles;
            
            if ~isempty(lBuildHookHandles)
                switch hookName
                  case 'before_buildApplication'
                    coder.coverage.BuildHook...
                        .dispatch_before_buildApplication...
                        (lBuildHookHandles, hookObj);
                    
                  case 'before_makefilebuilder_make'
                     [buildHookEnvVars, buildHookPathPrepends] = ...
                         coder.coverage.BuildHook.dispatch_before_makefilebuilder_make...
                         (lBuildHookHandles);
                     varargout{1} = buildHookEnvVars;
                     varargout{2} = buildHookPathPrepends;
                    
                  case 'after_makefilebuilder_make'
                    coder.coverage.BuildHook...
                        .dispatch_after_makefilebuilder_make...
                        (lBuildHookHandles, hookObj);
                    
                  case 'before_on_target_execution'
                    coder.coverage.BuildHook...
                        .dispatch_before_on_target_execution...
                        (lBuildHookHandles, componentArgs.getApplicationCodePath);
                    
                  case 'after_on_target_execution'
                    coder.coverage.BuildHook...
                        .dispatch_after_on_target_execution(lBuildHookHandles);
                  
                  case 'after_on_target_pause'
                    coder.coverage.BuildHook...
                        .dispatch_after_on_target_pause(lBuildHookHandles);

                  otherwise
                    assert(false, 'Unsupported build hook %s.', hookName);
                
                end
            end
        end
        
        function envCmds = getLaunchEnvironmentVars(~, componentArgs)
            % Return any setup commands for the launch environment 
            % (e.g. related to code coverage tool setup). Launch environment
            % setup commands may be specified by build hooks attached to the 
            % model
            
            lBuildHookHandles = componentArgs.getBuildHookHandles;
            if ~isempty(lBuildHookHandles)
                envCmds = coder.coverage.BuildHook...
                          .getLaunchEnvironmentVars(lBuildHookHandles);
            else
                envCmds = '';
            end
        end               
        
        function [isProfilingEnabled, ...
                  profilingMdlRefTgtType] = initializePerfProfiling(~, ...
                                                                    rootModel, ...
                                                                    inTheLoopType)
            % IsProfilingEnabled & ProfilingMdlRefTgtType will be used by
            % calls to slbuild_profile
            %            
            if bdIsLoaded(rootModel) || ...
                    inTheLoopType == rtw.pil.InTheLoopType.RLSSubsystem 
                % in the subsystem case we make sure that the hidden
                % reference model will be loadable, see 
                % rtw.pil.SILPILInterface.getRootModel
                isProfilingEnabled = ...
                         PerfTools.Tracer.enable('All Simulink Compile');    
            else
                isProfilingEnabled = false;
            end                                                
            %
            if inTheLoopType == rtw.pil.InTheLoopType.ModelBlock
                profilingMdlRefTgtType = 'RTW';
            else
                profilingMdlRefTgtType = 'NONE';
            end
        end
        
        function perfProfile(~, ...
                             desc, ...
                             startOrEnd, ...
                             isProfilingEnabled, ...
                             profilingMdlRefTgtType, ...
                             rootModel)
            
            if(isProfilingEnabled)
                targetName = profilingMdlRefTgtType;
                if(strcmp(targetName,'NONE'))
                    targetName = 'RTW';
                end
                PerfTools.Tracer.logSimulinkData('SLbuild', ...
                                        rootModel, ...
                                        targetName, ...
                                        desc, ...
                                        startOrEnd);            
            end
        end
               
        function lGlobalRegistry = createExecTimeGlobalReg(~, ...
                                         silPILInterface)  
            
            codedescriptorDir = silPILInterface.CodeDescriptorDir;
            lCodeDir = silPILInterface.getCodeDir;

            % Must reload infoStruct because silPILInterface.getInfoStruct may be stale.
            % With g1434846: retrieve this information directly and not via
            % infoStruct.
            infoStruct = coder.connectivity.ComponentInterfaceArtifacts.createLocatorAndGetInfoStruct(...
                codedescriptorDir, silPILInterface.getInTheLoopType(), false, silPILInterface.getTopModelForModelBlockPIL());
            
            modelRefBuildDirs = infoStruct.modelRefsBuildDirsAll;
            refModelsWithProfiling = ...
                setdiff(infoStruct.allModelsWithCodeProfiling, infoStruct.modelName);
            relativePathToAnchor = infoStruct.relativePathToAnchor;
            
            % Instantiate the global code instrumentation registry, which combines
            % registries for each model
            lCodeInstrInfo = silPILInterface.getSILPILWrapperUtils()...
                .getComponentArgs().getCodeInstrInfo();
            
            % Component registry for the topmost model
            if ~isempty(lCodeInstrInfo) && ...
                    any(strcmp(infoStruct.allModelsWithCodeProfiling, ...
                    infoStruct.modelName))
                % Execution time profiling is enabled and there must be a
                % profiling_info.mat in the instrumented folder
                lInstrSrcFolder = getInstrSrcFolder...
                    (lCodeInstrInfo, infoStruct.modelName);
                profilingInfoFile = fullfile(lCodeDir, lInstrSrcFolder, ...
                    'profiling_info.mat');
                assert(exist(profilingInfoFile, 'file')==2, ...
                    'profiling info file %s must exist', profilingInfoFile);
            else
                % For MW code coverage, check for existence of
                % profiling_info.mat file
                profilingInfoFile = fullfile(lCodeDir, 'profiling_info.mat');
                if ~isfile(profilingInfoFile)
                    profilingInfoFile = '';
                end
            end
            
            if ~isempty(profilingInfoFile)                
                fileContent = load(profilingInfoFile);
                componentRegistries = fileContent.componentRegistries;
            else
                componentRegistries = {};
            end
            
            pathToAnchor = fullfile...
                (lCodeDir, relativePathToAnchor);
            modelRefRegistries = coder.internal.getInstrRegistriesForRefModels...
                (modelRefBuildDirs, lCodeInstrInfo, ...
                               pathToAnchor, refModelsWithProfiling);
            lGlobalRegistry = coder.profile.ProbeGlobalRegistry...
                (infoStruct.modelName, pathToAnchor, relativePathToAnchor);
            
            % Identify shared library registries for Simulink Coverage
            sharedLibRegistries = {};
            if ~isempty(lCodeInstrInfo)
                lInstrObjFolder = getInstrObjFolder(lCodeInstrInfo);
                lBuildInfoInstrFile = fullfile(lCodeDir, lInstrObjFolder, ...
                    'buildInfo.mat');
                if isfile(lBuildInfoInstrFile)
                    lBuildInfoInstr = coder.make.internal.loadBuildInfo(lBuildInfoInstrFile);
                    [~, ~, lSharedLibPathsOriginalRelative] = ...
                        coder.coverage.getSharedBuildInfos(lBuildInfoInstr, lInstrObjFolder);
                    sharedLibRegistries = coder.internal.getInstrRegistriesForRefModels...
                        (lSharedLibPathsOriginalRelative, lCodeInstrInfo, ...
                        lBuildInfoInstr.Settings.LocalAnchorDir, {});
                end
            end
            
            % Ignore inactive registries.
            wrapperUtils = silPILInterface.getSILPILWrapperUtils;
            allRegistries = [componentRegistries modelRefRegistries sharedLibRegistries];
            allRegistries(~cellfun(@(reg) reg.isActive(wrapperUtils), allRegistries)) = [];

            lGlobalRegistry.addRegistries(allRegistries);            
        end
        
        function isEnabled = isPauseTimesEnabled(~, topContext)
            isEnabled = strcmp(get_param(topContext, 'EnablePauseTimes'), 'on');
        end
        
        function isDLL = isDLLTarget(~, ~)
            isDLL = false;
        end
        
        % update the code generation ReportInfo
        function updateReport(~, ...
                              silPILInterface, ...
                              buildInfo, ...
                              wrapperInfoReportOutOfDate, ...
                              lDefaultMexCompilerKey)        
            if silPILInterface.isModelBlockXIL
                % root model is always loaded for Top-Model & Model block
                assert(bdIsLoaded(silPILInterface.getRootModel), ...
                    'Root model must be loaded.');
                componentPath = silPILInterface.getFullComponentPath;                                
                % specify the appropriate ReportInfo explicitly via CodeDir                                                  
                reportInfo = rtw.report.getReportInfo(componentPath, ...
                                                      silPILInterface.getCodeDir);                
                % make sure reportInfo matches our code generation
                % scenario, otherwise we don't have a report to
                % append to                                    
                if silPILInterface.getInTheLoopType == rtw.pil.InTheLoopType.ModelBlock                    
                    expectedTargetType = 'RTW';                                        
                else
                    % top-model / block-based SIL/PIL
                    %
                    expectedTargetType = 'NONE';                    
                end
                assert(strcmp(reportInfo.ModelReferenceTargetType, expectedTargetType), ...
                       'Unexpected reportInfo.ModelReferenceTargetType');
                
                [reportSourceFiles, ...
                 reportHeaderFiles, ...
                 reportHostSourceFiles, ...
                 reportHostTimerFiles] = coder.connectivity.SimulinkInterface.getReportFiles(silPILInterface, buildInfo);
                reportInfoUpdated = coder.connectivity.SimulinkInterface.updateReportInfo(reportInfo, ...
                    reportSourceFiles, ...
                    reportHeaderFiles, ...
                    reportHostSourceFiles, ...
                    reportHostTimerFiles, ...
                    silPILInterface.getSimModeAcronym, ...
                    silPILInterface.getSILPILWrapperUtils.isProfilingEnabled);
                % wrapperInfoReportOutOfDate tells us whether the
                % report is out of date owing to wrapper
                % regeneration which may have updated the content
                % of the files.
                %
                % use the model's current value of GenerateReport rather than
                % the one cached at code generation time
                generateReport = get_param(silPILInterface.getRootModel, 'GenerateReport');
                if strcmp(generateReport, 'on') && ...
                        (reportInfoUpdated || wrapperInfoReportOutOfDate)
                    % updating the report can take a while if
                    % static code metrics are enabled; display a
                    % notification to the user
                    disp(DAStudio.message('PIL:pil:UpdatingReport', ...
                        silPILInterface.getSimModeAcronym));
                    % generate the HTML
                    if ~rtw.report.ReportInfo.featureReportV2
                        reportInfo.emitContents();
                        reportInfo.convertCode2HTML();
                    else
                        % V2 Uses Different Infrastructure
                        reportInfo.convertCode2HTML(); % TODO: Refactor this code to decouple XIL file generation from C_HTML tagging
                        rtw.report.generate(reportInfo.ModelName);
                    end
                    [~, ~, wrapperInfoTarget] = silPILInterface.getApplicationDependencies(lDefaultMexCompilerKey);
                    % clear the out of date flag
                    reportOutOfDate = false;
                    save(wrapperInfoTarget, 'reportOutOfDate', '-append');
                end
                % use the model's current value of LaunchReport rather than
                % the one cached at code generation time
                %
                % note: consider using "reportInfo.Config.LaunchReport"
                % which should be kept in sync with the setting on the
                % model
                launchReport = strcmp(get_param(silPILInterface.getRootModel, 'LaunchReport'), 'on');
                if launchReport
                    reportInfo.show;
                end
            end
        end
        
        function privateHeader = getPrivateHeader(~, silPILWrapperUtils)
            % return the header model_private.h if the file exists.
            privateHeader = [];
            if silPILWrapperUtils.shouldIncludePrivateHeader()
                topModel = silPILWrapperUtils.getSILPILInterface.getCodeName();
                if strcmp(silPILWrapperUtils.getParam('IsERTTarget'), 'on')
                    pkgFormat = silPILWrapperUtils.getParam('ERTFilePackagingFormat');
                    header = [topModel '_private.h'];
                    if strcmp(pkgFormat, 'Modular') && ...
                            isfile(fullfile(silPILWrapperUtils.getSILPILInterface.getCodeDir, header))
                        privateHeader = header;
                    end
                end
            end
        end
        
        function isStateLoggingEnabled = isStateLoggingEnabled(~, silPILWrapperUtils)
            isStateLoggingEnabled = ...
                silPILWrapperUtils.getCodeInfoUtils.UseMF0StateLogging;
            isStateLoggingEnabled = isStateLoggingEnabled && ...
                silPILWrapperUtils.isStateLoggingSupported() && ...
                strcmp(silPILWrapperUtils.getInstrumentationSettingsModelParam('SaveState'), 'on');
        end

        function isLoggingEnabled = isSignalLoggingEnabled(~, silPILWrapperUtils)
            isLoggingEnabled = strcmp(silPILWrapperUtils.getInstrumentationSettingsModelParam('SignalLogging'), 'on');
            if ~silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                isLoggingEnabled = isLoggingEnabled && ...
                    strcmp(silPILWrapperUtils.getParam('RTWCAPISignals'), 'on');
                % Note: The C-API configuration must be consistent 
                % throughout the model hierarchy therefore we only need 
                % check it at this level
            end
            
            % logging is not supported for protected models. Accelerator
            % mode does not show any warnings for this case, and thus
            % SIL/PIL won't. By setting isLoggingEnabled to false, any
            % signals that were marked for logging inside the protected
            % model will simply not show up in the logsout object.
            isLoggingEnabled = isLoggingEnabled && ...
                silPILWrapperUtils.isSignalLoggingSupported();
            assert(islogical(isLoggingEnabled), 'isLoggingEnabled must be a logical');            
        end
        
        function [loggingData, supportedDataInterfaces] = ...
                initializeStateLoggingData(~, silPILWrapperUtils, stateBlocks, ...
                    stateInfo, offset, inTheLoopType)
            % validate the states in the model to find the ones that we can
            % stream properly. Then, create an instance of
            % rtw.pil.stateloggingdata for each of them
            nStates = length(stateBlocks);
            loggingData = rtw.pil.StateLoggingData.empty(0, nStates);
            supportedDataInterfaces = coder.descriptor.DataInterface.empty(0, nStates);
            unsupportedDataInterfaces = cell(1, 0);
            supportedWithLimitationDataInterfaces = [];
            index = 1;
            
            for idx = 1:nStates
                [res, stateInfo(idx), fatalWarn, nonFatalWarn] = ...
                    coder.connectivity.SimulinkInterface.processStateInfo(...
                        silPILWrapperUtils, inTheLoopType, stateBlocks(idx), ...
                        offset+index, stateInfo(idx));
                if isempty(res)
                    unsupportedDataInterfaces{end + 1} = fatalWarn; %#ok<AGROW>
                    continue;
                end
                if ~isempty(nonFatalWarn)
                    supportedWithLimitationDataInterfaces(end+1).SignalInfo = nonFatalWarn.SignalInfo; %#ok<AGROW>
                    supportedWithLimitationDataInterfaces(end).Exception = nonFatalWarn.Exception;
                end
                loggingData(index) = res;
                supportedDataInterfaces(index) = stateBlocks(idx);
                index = index + 1;
            end
            toKeep = ~arrayfun(@isempty, supportedDataInterfaces);
            if any(toKeep)
                supportedDataInterfaces = supportedDataInterfaces(toKeep);
                loggingData = loggingData(toKeep);
            else
                loggingData = rtw.pil.StateLoggingData.empty;
                supportedDataInterfaces = [];
            end
            rootModel = silPILWrapperUtils.getComponentForDiagnostics();
            if ~isempty(unsupportedDataInterfaces)
                coder.connectivity.SimulinkInterface.throwUnsupportedStateLoggingWarning(...
                    unsupportedDataInterfaces, rootModel);
            end
            if ~isempty(supportedWithLimitationDataInterfaces)
                % throw warning for supported signals with limitations
                coder.connectivity.SimulinkInterface.throwSupportedWithLimitationLoggingWarning(...
                    supportedWithLimitationDataInterfaces, rootModel, ...
                    'PIL:pil:SupportedWithLimitationsLoggedStates', ...
                    'PIL:pil:UnsupportedLoggedState', false);
            end
        end
        
        function [loggingData, ...
                  modelLoggingInfoStruct, ...
                  supportedDataInterfaces, ...
                  loggingIODataTypes] = ...
                     initializeSignalLoggingData(~, ...
                                           silPILWrapperUtils, ...
                                           capiData, ...
                                           cachedXILBlockModelLoggingInfo, ...
                                           inTheLoopType, ...
                                           sigLogInfo)
            % reset LoggingData
            loggingData = rtw.pil.LoggingData.empty;
            unsupportedSignalLoggingWarningInfo = [];
            supportedWithLimitationsWarningInfo = [];
            supportedDataInterfaces = [];
            loggingIODataTypes = {};            
            
            % Determine the full list of signals that might be configured for
            % logging in the model.
            %
            % We must filter out anything showing up in the C-API that
            % is not also configured for signal logging.
            %
            % rootModel is the model associated with the component
            % under test (not the top-model for model ref)
            rootModel = silPILWrapperUtils.getSILPILInterface().getRootModel();
            rootModelForDiag = silPILWrapperUtils.getComponentForDiagnostics();
            if isempty(cachedXILBlockModelLoggingInfo)
                isTopModelXILSim = silPILWrapperUtils.getSILPILInterface.isTopModelXILSim;
                [modelLoggingInfo, unsupportedModelBlocks] = ...
                    coder.connectivity.SimulinkInterface.getModelLoggingInfoHook(inTheLoopType, isTopModelXILSim, rootModel);
                modelLoggingInfoStruct.ModelLoggingInfo = modelLoggingInfo;
                modelLoggingInfoStruct.UnsupportedModelBlocks = unsupportedModelBlocks;
                % create SID-based block path representation for comparison
                % with C API
                numSignals = length(modelLoggingInfo.Signals);
                PortURLs = cell(1, numSignals);
                for sigInfoIdx = 1:numSignals
                    sigInfo = modelLoggingInfo.Signals(sigInfoIdx);
                    % no support for multi-level block paths yet
                    assert(sigInfo.BlockPath.getLength == 1, ...
                        'Multi-level block paths should be filtered in getModelLoggingInfoHook');
                    % Note: A BlockPath with a "SubPath" is used
                    % to describe data within a block, such as
                    % Stateflow States or Stateflow Local Data.
                    % Currently, these SubPath signals are unsupported.
                    if isempty(sigInfo.BlockPath.SubPath)
                        sigInfoBlockPath = sigInfo.BlockPath.getBlock(1);                        
                        sid = get_param(sigInfoBlockPath, 'SID');
                        PortURLs{sigInfoIdx} = ...
                            char(Simulink.URL.PortURL([rootModel ':' sid], 'out', sigInfo.OutputPortIndex));
                    end
                end
                modelLoggingInfoStruct.PortURLs = PortURLs;
            else
                modelLoggingInfoStruct = cachedXILBlockModelLoggingInfo;
                modelLoggingInfo = modelLoggingInfoStruct.ModelLoggingInfo;
                unsupportedModelBlocks = modelLoggingInfoStruct.UnsupportedModelBlocks;
            end
            
            % throw warning for unsupported model block paths
            numUnsupportedModelBlocks = length(unsupportedModelBlocks);
            if numUnsupportedModelBlocks > 0
                modelBlocks = unsupportedModelBlocks{1};
                for modelBlockIdx = 2:numUnsupportedModelBlocks
                    modelBlocks = [modelBlocks ', ' unsupportedModelBlocks{modelBlockIdx}]; %#ok<AGROW>
                end
                % turn off the warning backtrace to clean up output
                warnState = warning('off', 'backtrace');
                oc = onCleanup(@()warning(warnState));
                DAStudio.warning('PIL:pil:UnsupportedMultiLevelBlockPath', ...
                    rootModelForDiag, ...
                    modelBlocks);
                clear('oc');
            end
            
            portURLs = modelLoggingInfoStruct.PortURLs;
            % Prepare C API blocks paths for fast vectorized strcmp
            % match.
            %
            % For SIL/PIL block (featured off) we have to do some
            % path manipulation to get a match.            
            isSILPILBlock = inTheLoopType == rtw.pil.InTheLoopType.Block;
            if ~silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                numCAPISignals = length(capiData.DataInterfaces);
                capiSIDs = cell(1, numCAPISignals);
                for capiSignalIdx = 1:numCAPISignals
                    dataInterface = capiData.DataInterfaces(capiSignalIdx);
                    if isSILPILBlock
                        % We assume:
                        % - A right-click subsystem build was performed
                        % - The subsystem is at the top-level of the model
                        %
                        % replace root path with model name
                        [~, remainder] = strtok(dataInterface.SID, ':');
                        capiSIDs{capiSignalIdx} = [rootModel remainder];
                    else
                        capiSIDs{capiSignalIdx} = dataInterface.SID;
                    end
                end
            else
                if isempty(sigLogInfo)
                    capiSIDs = cell(1,0);
                else
                    capiSIDs = {sigLogInfo.sid};
                end
            end
            isSigInfoProcessed = zeros(1, length(modelLoggingInfo.Signals));
            % loop and perform vectorized match against the C API paths
            %
            % This has been optimized to scale for e.g. 2000 C API
            % elements and 500 logging elements.
            %
            for sigInfoIdx = 1:length(modelLoggingInfo.Signals)
                sigInfo = modelLoggingInfo.Signals(sigInfoIdx);
                % Filter signals handled by the legacy Top-Model
                % SIL/PIL root I/O logging
                if coder.connectivity.SimulinkInterface.isLoggedByTopModelWrapper(...
                        silPILWrapperUtils.getSILPILInterface.isTopModelXILSim, sigInfo, rootModel)
                    % no warning required; mark as processed
                    isSigInfoProcessed(sigInfoIdx) = 1;
                else
                    sigInfoSID = portURLs{sigInfoIdx};
                    if ~isempty(sigInfoSID)
                        % match against C API in a vectorized manner
                        capiSignalIdx = find(strcmp(sigInfoSID, capiSIDs));
                        if ~isempty(capiSignalIdx)
                            assert(length(capiSignalIdx) == 1, ...
                                'Matched multiple regions with the same PortURL in C API!');
                            if silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                                sigInfoName = sigLogInfo(capiSignalIdx).name;
                                dataInterface = capiData(capiSignalIdx);
                            else
                                sigInfoName = '';
                                dataInterface = capiData.DataInterfaces(capiSignalIdx);
                            end
                            nextDataInterfaceIdx = length(supportedDataInterfaces) + 1;
                            if silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging
                                busIndex = sigLogInfo(capiSignalIdx).busIndex;
                                regionLength = sigLogInfo(capiSignalIdx).length;
                                regionOffset = sigLogInfo(capiSignalIdx).offset;
                            else
                                busIndex = -1;
                                regionLength = -1;
                                regionOffset = -1;
                            end
                            [result, fatalWarn, nonFatalWarn] = coder.connectivity.SimulinkInterface.processSigInfo(...
                                silPILWrapperUtils, sigInfo, dataInterface, nextDataInterfaceIdx, ...
                                inTheLoopType, sigInfoSID, sigInfoName, busIndex, regionLength, regionOffset);
                            if ~isempty(result)
                                % update loggingIODataTypes
                                loggingIODataTypes = [loggingIODataTypes result.LoggingIODatatypes]; %#ok<AGROW>
                                loggingData(end+1) = result.LoggingData; %#ok<AGROW>
                                if silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging && ...
                                        ~isempty(sigLogInfo(capiSignalIdx).signalNames)
                                    loggingData(end).setNameMapping(sigLogInfo(capiSignalIdx).signalNames);
                                end
                                if isempty(supportedDataInterfaces)
                                    supportedDataInterfaces = dataInterface;
                                else
                                    supportedDataInterfaces(end+1) = dataInterface; %#ok<AGROW>
                                end
                            end
                            if ~isempty(fatalWarn)
                                unsupportedSignalLoggingWarningInfo(end+1).SignalInfo = fatalWarn.SignalInfo; %#ok<AGROW>
                                unsupportedSignalLoggingWarningInfo(end).Exception = fatalWarn.Exception;
                            elseif ~isempty(nonFatalWarn)
                                supportedWithLimitationsWarningInfo(end+1).SignalInfo = nonFatalWarn.SignalInfo; %#ok<AGROW>
                                supportedWithLimitationsWarningInfo(end).Exception = nonFatalWarn.Exception;
                            end
                            % mark as processed
                            isSigInfoProcessed(sigInfoIdx) = 1;
                        end
                    end
                end
            end
            
            if silPILWrapperUtils.IsUpdateModelReferenceTargets
                % Don't issue warnings if only updating model reference
                % targets.  We should only issue the warnings when
                % the wrapper is being built for an actual simulation.
                %
                % This check happens before we check if the wrapper is up
                % to date so will run even when the wrapper is not
                % re-built.
                return;
            end
            
            unprocessedSigInfoIndices = find(~isSigInfoProcessed);
            sigInfos = modelLoggingInfo.Signals(unprocessedSigInfoIndices);
            sigInfos = silPILWrapperUtils.processUnloggedSignals(sigInfos);
            % warn for any modelLoggingInfo signals that were not processed
            for sigInfo = sigInfos
                % Handle SubPath specially
                if ~isempty(sigInfo.BlockPath.SubPath)
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                        'UnsupportedBlockPathSubPath');
                else
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                        'NoMatchingCAPISignal');
                end
                e = MException(id, message);
                unsupportedSignalLoggingWarningInfo(end+1).SignalInfo = sigInfo; %#ok<AGROW>
                unsupportedSignalLoggingWarningInfo(end).Exception = e;
            end
            % throw warning for unsupported signals
            coder.connectivity.SimulinkInterface.throwUnsupportedSignalLoggingWarning(unsupportedSignalLoggingWarningInfo, rootModelForDiag);
            % throw warning for supported signals with limitations
            if ~isempty(supportedWithLimitationsWarningInfo)
                coder.connectivity.SimulinkInterface.throwSupportedWithLimitationLoggingWarning(...
                    supportedWithLimitationsWarningInfo, rootModelForDiag, ...
                    'PIL:pil:SupportedWithLimitationsLoggedSignals', ...
                    'PIL:pil:UnsupportedLoggedSignal', true);
            end
        end
        
        function lCodeInstrInfo = getCodeInstrInfo(~, silPILInterface, lTopModel, lBuildHooks, ...
                                                      lCodeExecutionProfilingEnabled)

            isPWS = strcmp(silPILInterface.getInfoStruct.configSet.get_param...
                ('PortableWordSizes'), 'on');
            
            % If this class is being used in update_model_reference_targets
            % via Simulink.ModelReference.internal.XILSfunction, in a pure
            % code generation workflow (CTRL+B, rtwbuild), then
            % CodeInstrBuildArgs will have been constructed with
            % isSILAndPWS = false.  The property SuppressIsSILAndPWS on
            % silPILInterface is used to indicate this, so that a
            % consistent CodeInstrBuildArgs object can be created, so that
            % the file profiling_info.mat that was generated during
            % code generation can be found.
            isSILAndPWS = silPILInterface.isSIL && isPWS && ~silPILInterface.SuppressIsSILAndPWS;
            
            lCodeCoverageSettings = coder.coverage.CodeCoverageSettings(lBuildHooks);
            lCodeCoverageSpec = [];
            if ~strcmp(lCodeCoverageSettings.CoverageTool, 'None')
                lCodeCoverageSpec = coder.internal.CodeInstrSpecCoverageSL...
                    (lCodeCoverageSettings, lTopModel);
            end
            
            % Special case for protected model reference XIL simulation and code execution
            % profiling enabled: in this scenario, profiling is allowed, but
            % there is no "instrumented" sub-folder for the protected model
            % object code. 
            lCreateInstrumentedFolderForTaskProfiling = ...
                lCodeExecutionProfilingEnabled && ~silPILInterface.IsModelProtected;
                            
            lCodeInstrInfo = coder.internal.slCreateCodeInstrBuildArgs...
                (silPILInterface.getInfoStruct.modelName, ...
                 isSILAndPWS, ... 
                 lCodeCoverageSpec, ...
                 lCreateInstrumentedFolderForTaskProfiling,...
                 silPILInterface.getInfoStruct.allModelsWithCodeProfiling, ...
                 silPILInterface.getInfoStruct.modelRefsAll, ...
                 silPILInterface.getInfoStruct.protectedModelRefs);
        end
        
        
        function componentArgs = createComponentArgs(this, ...
                                                     componentPath, ...
                                                     componentCodePath, ...
                                                     componentCodeName, ...
                                                     applicationCodePath, ...
                                                     silPILInterface,...
                                                     lTopModel, ...
                                                     lBuildHooks, ...
                                                     lCodeExecutionProfilingEnabled)

            % create the componentArgs object
            componentArgs = rtw.connectivity.ComponentArgs(componentPath, ...
                                                           componentCodePath, ...
                                                           componentCodeName, ...
                                                           applicationCodePath);
            
            lCodeInstrInfo = this.getCodeInstrInfo...
                (silPILInterface, lTopModel, lBuildHooks, lCodeExecutionProfilingEnabled);
            componentArgs.setCodeInstrInfo(lCodeInstrInfo);
            componentArgs.setTopModel(lTopModel);
            componentArgs.setBuildHooks(lBuildHooks);
            setInTheLoopType(componentArgs, getInTheLoopType(silPILInterface));
            cfg = Simulink.fileGenControl('getConfig');
            componentArgs.setAnchorFolder(cfg.CodeGenFolder);
        end

        function clientInterface = createCoreClientInterface(this)
            if isempty(this.CoreSimulinkInterface)
                % If a CoreSimulinkInterface has not already been created,
                % create it.
                this.CoreSimulinkInterface = coder.connectivity.CoreSimulinkInterface;
            end
            
            % Return the cached CoreSimulinkInterface.
            clientInterface = this.CoreSimulinkInterface;
        end
                 
        function startMessage = getSILStartMessage(~, componentPath)
            startMessage = ['### Starting SIL ' ...
                        'simulation for component: ' componentPath];
        end
                
        function stopMessage = getSILStopMessage(~, componentPath)
            stopMessage = ['### Stopping SIL '...
                            'simulation for component: ' componentPath];            
        end  
        
        function longLongInfo = getLongLongInfo(~)
            hostWordLengths = rtwhostwordlengths; 
            longLongInfo.LongNumBits = hostWordLengths.LongNumBits;
            longLongInfo.LongLongNumBits = hostWordLengths.LongLongNumBits;
            longLongInfo.LongLongMode = hostWordLengths.LongLongMode;            
        end
        
        function val = ciIsMATLABCodeGen(~)
        % Not a MATLAB Coder build
            val = false;
        end
        
        function modelLib = updateModelLibExt(~, modelLib)
           %Update modelLib file extension if necessary.
        end
        
        function extraPaths = additionalSystemPaths(~, ~)
            extraPaths = {};
        end
        % Determine if a DataElement with Matrix Type in CodeInfo is
        % implemented as an array in generated code
        function isMatrix = isArray(~, dimensions)
            % Simulink Coder often lists items as having matrix type in
            % CodeInfo when they are actually implemented as scalars. As a
            % result have to additionally use whether the total number of
            % dimensions is greater than 1 to identify items implemented as
            % Arrays.
            % NOTE: For AUTOSAR, there is a special case when codeInfo will
            % describe a type as ArrSizeOne. For this case, isArray will
            % incorrectly return false, but rtw.pil.TargetInterface.isArrSizeOne
            % will return true. These APIs should be consolidated to void
            % confusion.
            isMatrix = prod(dimensions) > 1;
        end
        
        % For Simulink case always synchronize any I/O during one shot calls
        function syncIODuringOneShot = syncIODuringOneShot(~)
            syncIODuringOneShot = true;
        end

        % Determine whether internal code coverage support is enabled
        function isEnabled = isMWCodeCoverageEnabled(~)
            isEnabled = true;
        end

        % Validate Use of Global Data Stores
        function validateGlobalDataStores(~, wrapperUtils, inTheLoopType, silPILInterface, dataStoreList)
            if (inTheLoopType==rtw.pil.InTheLoopType.ModelBlock) && ...
                    wrapperUtils.isValidParam('ConcurrentTasks') && ...
                    strcmp(wrapperUtils.getParam('ConcurrentTasks'), 'on') && ...
                    ~isempty(dataStoreList)
                %Check for Global Data Stores in Model blocks executed in the
                %Concurrent Task Context. These aren't supported because the
                %Mutex guarding the value is only defined in the parent model.
                 DAStudio.error('PIL:pil:ConcurrentTasksGlobalDSM', ...
                    silPILInterface.getRootModel, dataStoreList(1).GraphicalName, ...
                    dataStoreList(1).GraphicalName);
            end
        end
        
        % Determine if extended timeout is needed
        function extendTimeout = extendTimeOutForSIL(~, isSIL, config)
            extendTimeout = false;
            if isSIL
                if strcmp(config.getParam('GenerateGPUCode'), 'CUDA') && ...
                        strcmp(config.getParam('DLTargetLibrary'), 'tensorrt')
                    %Extend timeout for Simulink Coder SIL, if performing
                    %Deep Learning with tensor library as it takes a long
                    %time to process                    
                    extendTimeout = true;
                end                
            end
        end
        
        % Open callback for the rtiostream
        function rtIOStreamPreOpen(~, rtIOStream, extendTimeout)
            if extendTimeout
                %Extend receive time out
                rtIOStream.setRecvTimeout(200);
            end
        end
    end        
    
    methods (Static)
       function silPILInterface = getSILPILInterface(inTheLoopType,...
                                                  blockPath)            
           % retrieve the PIL interface 
           handler = coder.connectivity.PILDataHandler.createHandler(inTheLoopType);
           silPILInterface = handler.getPILData(blockPath);
       end      
        
       % hyperlink call back function to hilite a block
       function blockPathHyperlinkCallback(blockPath)
           % hide any errors owing to e.g. unloaded models
           try
               hilite_system(blockPath);
           catch e     %#ok<NASGU>
           end
       end
       
       function blocks = findRootLevelBlocks(componentPath, blockType)
            blocks = find_system(componentPath, ...
                'FollowLinks', 'on', ...
                'LookUnderMasks', 'all', ...
                'SearchDepth', 1, ...
                'BlockType', blockType);
       end
               
       % create a new model for Top-model SIL or SIL block
       function varargout = createModelFromOriginal(origModel, newModel)
           narginchk(2, 2);
           nargoutchk(0, 1);
           validateattributes(origModel, {'char'}, {});
           if ~isempty(newModel)
               validateattributes(newModel, {'char'}, {});
           end
           if isempty(newModel)
               newModel = sltemplate.internal.getUntitledSystemName;
           end
           h = new_system(newModel, 'FromTemplate','factory_default_model');
           if nargout == 1
               varargout{1} = h;
           end
           % copy data dictionary setting to wrapper model
           origModel_dd = get_param(origModel,'DataDictionary');
           set_param(newModel,'DataDictionary',...
               origModel_dd);
           
           % take broker config and add all implicit source as explicit
           % read old model's entire list of sources
           currentMdlObj = get_param(origModel,'slobject');
           currentMdlBroker = currentMdlObj.getBroker();
           currentMdlConfig = currentMdlBroker.getActiveConfig;
           currentSrcList = currentMdlConfig.getAllDefinitionSourceList();
           
           % write to explicit sources in new model's broker
           newMdlObj = get_param(newModel,'slobject');
           newMdlBroker = newMdlObj.getBroker();
           newMdlConfig = newMdlBroker.getActiveConfig;
           tmpMf0Model = mf.zero.Model.createTransientModel();
           
           for i=1:length(currentSrcList)
               currentSrc = currentSrcList{i};
               [~, filename, ~] = fileparts(currentSrc);
               if bdIsLoaded(filename)
                   isLibrary = bdIsLibrary(filename);
               else
                   mdlInfo = Simulink.MDLInfo(currentSrc);
                   isLibrary = mdlInfo.IsLibrary();
               end
               if isLibrary
                   libDDs = currentMdlBroker.lookUpDictionariesForLibrary(currentSrc);
                   for aLibCell = libDDs
                      aLibSrc = [which(aLibCell{1}) '#BROKEREDSLDD'];
                      s = newMdlConfig.addExplicitExternalSource(aLibSrc, tmpMf0Model);
                      assert(s.Success, 'Failed to add external source %s', aLibSrc);
                   end
               else
                   s = newMdlConfig.addExplicitExternalSource(currentSrcList{i}, tmpMf0Model);
                   assert(s.Success, 'Failed to add external source %s', currentSrcList(i));
               end
           end
           
           % Get config set from original model
           cs = getActiveConfigSet(origModel);
           
           % Copy the active config set and change the copied name
           tempCs = cs.copy;
           tempCs.Name =  'Config1';
           
           attachConfigSet(newModel,tempCs);
           setActiveConfigSet(newModel, tempCs.Name);
           
           % Copy model work space variables to wrapper model if any
           sws = get_param(origModel, 'ModelWorkspace');
           if ~isempty(sws.data)
               dws = get_param(newModel, 'ModelWorkspace');
               for i=1:length(sws.data)
                   var = sws.data;
                   dws.assignin(var(i).Name, var(i).Value);
               end
           end
           
           % copy top-level data type override settings
           set_param(newModel, 'DataTypeOverride', ...
               get_param(origModel, 'DataTypeOverride'));
           set_param(newModel, 'DataTypeOverrideAppliesTo', ...
               get_param(origModel, 'DataTypeOverrideAppliesTo'));
           
           % copy model verbosity setting           
           set_param(newModel, 'SILPILVerbosity', ...
               get_param(origModel, 'SILPILVerbosity'));
       end
       
        function xilNoWrapper = XilNoWrapper(~)
            xilNoWrapper = slfeature('XilNoWrapper');
        end
        
        function useCodeType = useCodeType(~)
            % CodeInfo Implementation.CodeType is available for Simulink workflows
            useCodeType = true;
        end
        
        function obj = forUpdateModelReferenceTargets()
            % CODER.CONNECTIVITY.SIMULINKINTERFACE.FORUPDATEMODELREFERENCETARGETS
            %   Static constructor to use when called via
            %   update_model_reference_targets.
            obj = coder.connectivity.SimulinkInterface;
            
            % If updating model ref targets, do not check for empty
            % interfaces on RTW.DataImplementation objects.
            obj.CoreSimulinkInterface = coder.connectivity.CoreSimulinkInterface.withCheckForEmptyInterfaceDisabled;
        end
        
        function blockHandle = getResolvedBlockHandle(block)
            if ischar(block)
                blockHandle = getSimulinkBlockHandle(block);
                if blockHandle == -1
                    %Strip off final '/<blockName>'. When a synthetic
                    %system is added it has a duplicated name. Try just
                    %stripping off that extra block path to see if the
                    %block handle exists. Just error if it's still not
                    %correct.
                    blockHandle = getSimulinkBlockHandle(regexprep(block,'/\w+$',''));
                end
            else
                blockHandle = block;
            end
        end
    end
    
    methods (Static, Access = 'private')
        function lBuildHooks = getBuildHooks(silPILWrapperUtils, lTopModel)
            
            if ~isempty(silPILWrapperUtils)
                % Top model may not be open for a SIL/PIL block simulation
                lBuildHooks = silPILWrapperUtils.getInstrumentationSettingsModelBuildHooks();
            else
                % SILPILInterface is currently being constructed and the top model must be
                % open at this stage
                lBuildHooks = coder.coverage.getBuildHooks(lTopModel);
            end
        end        
        
        
        function binfoMATFile = getBinfoSIMMATFile(codeName)
            %
            % derive mdlref binfo.mat path from codeName
            % we know to look in the "sim" dir
            %
            fileGenCfg = Simulink.fileGenControl('getConfig');
            rootFolder = fileGenCfg.CacheFolder;
            binfoMATFile = fullfile(rootFolder, ...
                'slprj', ...
                'sim', ...
                codeName, ...
                'tmwinternal', ...
                'binfo_mdlref.mat');
        end  
        
        function link = createHyperlinkToUnsupportedBlock(blockPath)
            link = targets_hyperlink_manager('new', ...
                blockPath, ...
                @coder.connectivity.SimulinkInterface.blockPathHyperlinkCallback, ...
                blockPath);
        end
        
        function addVarToSingleOutput(inTheLoopType, topmodel, blockPath, ...
                                      lOriginalComponentName, ...
                                      workspaceVarName, varargin)
                                  
            if  (inTheLoopType == rtw.pil.InTheLoopType.ModelBlock) || ...
                (inTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone)
                % Model block case
                varPath = topmodel;
            elseif ~strcmp(fliplr(strtok(fliplr(blockPath),'/')), strtok(lOriginalComponentName, '/'))
                % SIL/PIL block case. There may be multiple SIL or PIL blocks and the execution
                % profile variable name is set in the original model that the
                % block was generated from. To identify the original model, we
                % include lOriginalComponentName in the variable description
                % that appears in the name clash error message
                varPath = sprintf('%s > %s', blockPath, ...
                    strtok(lOriginalComponentName, '/'));
            else
                % Top model case
                varPath = topmodel;
            end
            h = get_param(topmodel, 'Object');
            varDescription = DAStudio.message('PIL:pil:ProfileVariableDescription', ...
                                              varPath);
            h.addVarToSimulationOutput(workspaceVarName, varDescription, varargin{:});
            
        end
        
        function [sourceFiles, headerFiles, hostSourceFiles, hostTimerFiles] = getReportFiles(silPILInterface, buildInfo)
            % determine lists of files to add to the code generation report
            % for this In-the-Loop configuration            
            sourceFiles = {};
            headerFiles = {};
            hostSourceFiles = {};
            hostTimerFiles = {};
            % source files
            src = buildInfo.getSourceFiles(true, true);
            hostTimer_srcfiles = {'host_timer_csa_x86.c', 'host_timer_mac.c',...
                'host_timer_x86.c','host_timer_x86_lcc.c'};
            hostTimer_srcfiles = fullfile(matlabroot, 'toolbox','coder','profile','src',hostTimer_srcfiles);
            for i=1:length(src)
                srcFile = src{i};
                assert(coder.coverage.CodeCoverageHook.isAbsPath({srcFile}) && exist(srcFile, 'file'), ...
                       'srcFile %s must exist and have an absolute path', srcFile);                
                if ismember(srcFile,hostTimer_srcfiles)
                    hostTimerFiles{end+1} = srcFile; %#ok<AGROW>
                else
                    sourceFiles{end+1} = srcFile; %#ok<AGROW>
                end
            end            
            % header files
            inc = buildInfo.getIncludeFiles(true, true);
            for i=1:length(inc)
                incFile = inc{i};
                assert(coder.coverage.CodeCoverageHook.isAbsPath({incFile}) && exist(incFile, 'file'), ...
                       'incFile %s must exist and have an absolute path', incFile);
                headerFiles{end+1} = incFile; %#ok<AGROW>
            end
            % s-function
            [~, sfunSourcePath] = silPILInterface.getPILSfunction;
            hostSourceFiles{end+1} = sfunSourcePath;
        end                   
        
        
        
        function filterProfilingStubFiles(reportInfo)
        % Filter instrumentation stubs from Code Metrics report: 
        % this avoids a failure arising from duplicate function
        % names
            fileToFilterSuffix = coder.profile.ExecTimeConfig.EmptyTimerSrcName;
            
            lTag = 'Instrumentation:Stubs';
            lGroup = 'other';
            
            filesToFilterExt = {'.c', '.h', '.cpp', '.hpp'};
            
            % getFileInfo returns full-path while FileInfo may contain
            % $(BuildDir)
            fileInfo = reportInfo.getFileInfo;
            reportInfoPaths = cell(size(fileInfo));
            [reportInfoPaths{1:length(reportInfoPaths)}] = ...
                deal(fileInfo(:).Path);
            reportInfoPaths = unique(reportInfoPaths);

            for i=1:length(reportInfoPaths)
                lPath = reportInfoPaths{i};
                dirFilt = dir(fullfile(lPath, [fileToFilterSuffix '.*']));
                for ii1=1:length(dirFilt)
                    [~, f, e] = fileparts(dirFilt(ii1).name);
                    if any(strcmp(e, filesToFilterExt))
                        lFileName = [f e];
                        if strncmp(e, '.h', 2)
                            lType = 'header';
                        else 
                            lType = 'source';
                        end
                        if any(strcmp({reportInfo.FileInfo.FileName},lFileName))
                            reportInfo.updateFileInfoTag(lFileName, lTag);
                        else
                            reportInfo.addFileInfo(lFileName, lGroup, lType, lPath, lTag);
                        end
                    end
                end
            end
        end
        
        
        function updated = updateReportInfo(reportInfo, ...
                                            reportSourceFiles, ...
                                            reportHeaderFiles, ...
                                            reportHostSourceFiles, ...
                                            reportHostTimerFiles, ...
                                            silPILTag,...
                                            isProfilingEnabled)
            % if necessary, update ReportInfo with files comprising the
            % SIL/PIL wrapper
            inTheLoopTag = 'In-the-Loop';            
            silPilSpecificTag = [inTheLoopTag ':' silPILTag];
            hostSpecificTag = [inTheLoopTag ':Host'];       
            hostTimerSpecificTag = [inTheLoopTag ':HostTimer'];
            fileInfoGroup = rtw.pil.getSILPILFilesReportGroupName;
            
            % get set of previous In-the-Loop files
            currentFileInfos = reportInfo.getTaggedFiles(inTheLoopTag);           
            currentFiles = {};
            for idx = 1:length(currentFileInfos)
                currentFileInfo = currentFileInfos(idx);
                currentFiles{end+1} = fullfile(currentFileInfo.Path, ...
                    currentFileInfo.FileName);                 %#ok<AGROW>
            end
                        
            % combine files for comparison
            newFiles = [reportSourceFiles ...
                        reportHeaderFiles ...
                        reportHostSourceFiles ...
                        reportHostTimerFiles];
            
            % check for any differences between these sets
            if isempty(setxor(currentFiles, newFiles))
                updated = false;
            else
                updated = true;
                % clear out any previous In-the-Loop files
                reportInfo.removeTaggedFiles(inTheLoopTag);
                % add source files
                src = reportSourceFiles;
                for i=1:length(src)
                    srcFile = src{i};
                    [p, f, e] = fileparts(srcFile);
                    reportInfo.addFileInfo([f e], ...
                        fileInfoGroup, ...
                        'source', ...
                        p, ...
                        silPilSpecificTag);
                end                
                % add host source files
                src = reportHostSourceFiles;
                for i=1:length(src)
                    srcFile = src{i};
                    [p, f, e] = fileparts(srcFile);
                    reportInfo.addFileInfo([f e], ...
                        fileInfoGroup, ...
                        'source', ...
                        p, ...
                        hostSpecificTag);
                end
                % add host source files
                files = reportHostTimerFiles;
                for i=1:length(files)
                    file = files{i};
                    [p, f, e] = fileparts(file);
                    reportInfo.addFileInfo([f e], ...
                        fileInfoGroup, ...
                        'source', ...
                        p, ...
                        hostTimerSpecificTag);
                end                
                % add wrapper header files
                inc = reportHeaderFiles;
                for i=1:length(inc)
                    incFile = inc{i};
                    [p, f, e] = fileparts(incFile);
                    reportInfo.addFileInfo([f e], ...
                        fileInfoGroup, ...
                        'header', ...
                        p, ...
                        silPilSpecificTag);
                end
                
                if isProfilingEnabled
                    
                    coder.connectivity.SimulinkInterface.filterProfilingStubFiles...
                        (reportInfo);
                    
                end
                        
                % persist the data, always keeping
                % reportInfo.mat in sync with the ReportInfo
                % object we modified.                
                reportInfo.saveMat;
            end
        end
        
        function throwUnsupportedStateLoggingWarning(unsupportedStateWarningInfo, rootModel)
            totalNumStates = length(unsupportedStateWarningInfo);
            msgList = '';
            maxLength = 8000;
            emptyMsg = DAStudio.message('PIL:pil:UnsupportedLoggedStates', rootModel, '');
            busesNotSupported = cell(1, 0);
            actLength = length(emptyMsg);
            for warnInfoIdx = 1:totalNumStates
                warnInfo = unsupportedStateWarningInfo{warnInfoIdx};
                assert(warnInfo.SignalInfo.BlockPath.getLength == 1, 'Multi-level block paths should be filtered.');                        
                blockPath = warnInfo.SignalInfo.BlockPath.getBlock(1);
                if strcmp(warnInfo.Exception.identifier, 'PIL:pil:UnsupportedLogBusState') && ...
                        any(strcmp(blockPath, busesNotSupported))
                    continue;
                end
                busesNotSupported{end+1} = blockPath; %#ok<AGROW>
                link = coder.connectivity.SimulinkInterface.createHyperlinkToUnsupportedBlock(blockPath);
                tmpMsg = message('PIL:pil:UnsupportedLoggedState', ...
                            link, ...
                            warnInfo.Exception.message);
                msgStr = MSLDiagnostic(tmpMsg).message;
                msgStrLength = length(msgStr);
                if (actLength + msgStrLength) < maxLength
                    actLength = actLength + msgStrLength;
                    msgList = sprintf('%s\n%s', msgList, msgStr);
                end
            end
            if ~isempty(msgList)
                warnState = warning('off', 'backtrace');
                oc = onCleanup(@()warning(warnState));
                DAStudio.warning('PIL:pil:UnsupportedLoggedStates', rootModel, msgList);
                % reset
                clear('oc');                
            end
        end
        
        function throwUnsupportedSignalLoggingWarning(unsupportedSignalLoggingWarningInfo, rootModel)
            % to avoid flooding the user with many warnings, 
            % all unsupported signals are grouped into a single warning
            % which is consistent with current best practice for similar
            % circumstances
            %
            % NOTE: large warnings are split to avoid issues with maximum 
            % warning length per g834835
            messageList = '';    
            % cautiously step back a little from the actual limit of 8191
            maxWarningSize = 8000;
            % get baseline message list with empty messageList                        
            noMatchCAPI = DAStudio.message('PIL:pil:NoMatchingCAPISignal');                        
            baselineMsg = DAStudio.message('PIL:pil:UnsupportedLoggedSignals', ...
                rootModel, ...
                messageList, ...
                noMatchCAPI);            
            messageListSizeLimit = maxWarningSize - length(baselineMsg);                                    
            totalNumSignals = length(unsupportedSignalLoggingWarningInfo);
            for warnInfoIdx = 1:totalNumSignals
                warnInfo = unsupportedSignalLoggingWarningInfo(warnInfoIdx);                                                
                sigInfo = warnInfo.SignalInfo;
                assert(sigInfo.BlockPath.getLength == 1, ...
                       'Multi-level block paths should be filtered.');                        
                blockPath = sigInfo.BlockPath.getBlock(1);
                % hilite the unsupported block via a hyperlink (new lines
                % have already been removed from sigInfo)
                link = coder.connectivity.SimulinkInterface.createHyperlinkToUnsupportedBlock(blockPath);
                switch warnInfo.Exception.identifier
                    case 'PIL:pil:UnsupportedBlockPathSubPath'
                        msgStr = DAStudio.message('PIL:pil:UnsupportedLoggedSignalWithSubPath', ...
                            link, ...
                            sigInfo.BlockPath.SubPath, ...
                            warnInfo.Exception.message);
                    otherwise
                        tmpMsg = message('PIL:pil:UnsupportedLoggedSignal', ...
                            link, ...
                            sigInfo.OutputPortIndex, ...
                            warnInfo.Exception.message);
                        msgStr = MSLDiagnostic(tmpMsg).message;
                end                    
                % build candidate message list
                extendedMessageList = sprintf('%s\n%s', ...
                    messageList, ...
                    msgStr);                
                if length(extendedMessageList) > messageListSizeLimit
                    % throw the original message list
                    throwWarning(messageList);
                    % reset messageList
                    messageList = msgStr;
                else
                    % accept extended message list
                    messageList = extendedMessageList;
                end                                                                                
            end
            % throw message if necessary
            if ~isempty(messageList)
                throwWarning(messageList);            
            end
            
            % nested function to throw the warning
            function throwWarning(messageListArg)
                % validate the message splitting algorithm
                msg = DAStudio.message('PIL:pil:UnsupportedLoggedSignals', ...
                    rootModel, ...
                    messageListArg, ...
                    noMatchCAPI);
                assert(length(msg) <= maxWarningSize, ...
                       'UnsupportedLoggedSignals warning was split incorrectly!');                                                
                % turn off the warning backtrace to clean up output
                warnState = warning('off', 'backtrace');
                oc = onCleanup(@()warning(warnState));
                DAStudio.warning('PIL:pil:UnsupportedLoggedSignals', ...
                    rootModel, ...
                    messageListArg, ...
                    noMatchCAPI);
                % reset
                clear('oc');                
            end
        end                 
        
        function throwSupportedWithLimitationLoggingWarning(warningInfo, rootModel, rootMsgId, childMsgId, hasPortIdx)
            messageList = '';
            % cautiously step back a little from the actual limit of 8191
            maxWarningSize = 8000;
            % get baseline message list with empty messageList
            baselineMsg = DAStudio.message(rootMsgId', rootModel, messageList);
            messageListSizeLimit = maxWarningSize - length(baselineMsg);
            totalNumSignals = length(warningInfo);
            for warnInfoIdx = 1:totalNumSignals
                warnInfo = warningInfo(warnInfoIdx);
                sigInfo = warnInfo.SignalInfo;
                assert(sigInfo.BlockPath.getLength == 1, ...
                    'Multi-level block paths should be filtered.');
                blockPath = sigInfo.BlockPath.getBlock(1);
                % hilite the unsupported block via a hyperlink (new lines
                % have already been removed from sigInfo)
                link = coder.connectivity.SimulinkInterface.createHyperlinkToUnsupportedBlock(blockPath);
                if hasPortIdx
                    tmpMsg = message(childMsgId, ...
                        link, ...
                        sigInfo.OutputPortIndex, ...
                        warnInfo.Exception.message);
                else
                    tmpMsg = message(childMsgId, link, warnInfo.Exception.message);
                end
                msgStr = MSLDiagnostic(tmpMsg).message;
                
                % build candidate message list
                extendedMessageList = sprintf('%s\n%s', ...
                    messageList, ...
                    msgStr);
                if length(extendedMessageList) > messageListSizeLimit
                    % throw the original message list
                    throwWarning2(messageList);
                    % reset messageList
                    messageList = msgStr;
                else
                    % accept extended message list
                    messageList = extendedMessageList;
                end
            end
            % throw message if necessary
            if ~isempty(messageList)
                throwWarningWithLimitations(messageList, rootMsgId);
            end
            
            % nested function to throw the warning
            function throwWarningWithLimitations(messageListArg, rootMsgId)
                % validate the message splitting algorithm
                msg = DAStudio.message(rootMsgId, rootModel, messageListArg);
                assert(length(msg) <= maxWarningSize, ...
                    'UnsupportedLoggedSignals warning was split incorrectly!');
                % turn off the warning backtrace to clean up output
                warnState = warning('off', 'backtrace');
                oc = onCleanup(@()warning(warnState));
                DAStudio.warning(rootMsgId, rootModel, messageListArg);
                % reset
                clear('oc');
            end
        end
        
        function isLoggedByTopModelWrapper = isLoggedByTopModelWrapper(isTopModelXILSim, ...
                                                                       sigInfo, ...
                                                                       rootModel) 
           % Root level I/O is currently logged via the Top-Model
           % wrapper mechanism
           isLoggedByTopModelWrapper = false;
           
           % check In-the-Loop type
           if isTopModelXILSim
               % top-model wrapper will only process block paths at the 
               % top-level, and not Stateflow SubPaths
               assert(sigInfo.BlockPath.getLength == 1, ...
                      'Multi-level block paths should be filtered.');                        
               if isempty(sigInfo.BlockPath.SubPath)
                   blockPath = sigInfo.BlockPath.getBlock(1);                   
                   % check for root level block path
                   if strcmp(get_param(blockPath, 'Parent'), ...
                           rootModel)
                       % simple check for inport
                       if strcmp(get_param(blockPath, 'BlockType'), ...
                               'Inport')
                           isLoggedByTopModelWrapper = true;                           
                       else
                           % check to see if block port is connected to an
                           % outport
                           ports = get_param(blockPath, 'PortConnectivity');
                           for idx = 1:length(ports)
                               port = ports(idx);
                               if strcmp(port.Type, int2str(sigInfo.OutputPortIndex)) && ...
                                       ~isempty(port.DstBlock)
                                   % DstBlock will be a vector for branched
                                   % signals
                                   if any(strcmp(get_param(port.DstBlock, 'BlockType'), ...
                                           'Outport'))
                                       isLoggedByTopModelWrapper = true;                                       
                                   end
                                   % we processed the correct port
                                   break;
                               end
                           end
                       end
                   end
               end
           end
        end
       
        function [modelLoggingInfo, unsupportedModelBlocks] = ...
                getModelLoggingInfoHook(inTheLoopType, isTopModelXILSim, rootModel)            
            if isTopModelXILSim || inTheLoopType == rtw.pil.InTheLoopType.Block                      
                % Owing to incremental code generation, Data Logging
                % information of the model is up to date.
                %
                % We only want to regenerate the wrapper if the set of signals
                % marked for logging in the model changes (note that this
                % may not trigger code generation) or if signal
                % logging overrides change.   The LoggingData
                % checksum will trigger regeneration.
                %
                % Note that for Top-Model SIL/PIL, owing to the
                % hidden wrapper model, we cannot rely on the
                % Simulink logging infrastructure to automatically
                % filter signals that are not being logged in the
                % current override configuration.   Therefore, we
                % explicitly filter them out from the wrapper.
                % This is ok assuming a single instance of the
                % model is executing.
                %
                % get ModelLoggingInfo
                modelLoggingInfo = get_param(rootModel, 'DataLoggingOverride');
                if strcmp(modelLoggingInfo.LoggingMode, 'LogAllAsSpecifiedInModel')
                    % create a new ModelLoggingInfo object that covers all
                    % signals in the hierarchy
                    ignoreRefModels = false;
                    modelLoggingInfo = coder.connectivity.SimulinkInterface.createLoggingInfoFromModel(...
                        rootModel, ignoreRefModels);
                else
                    assert(strcmp(modelLoggingInfo.LoggingMode, 'OverrideSignals'), ...
                        'Unsupported LoggingMode!');
                    % automatically handle renamed paths, etc.
                    modelLoggingInfo = modelLoggingInfo.verifySignalAndModelPaths('warnAndRemove');
                end
                % find unsupported Model blocks based on LogAsSpecifiedByModel
                rootModelMatchVector = strcmp(rootModel, modelLoggingInfo.LogAsSpecifiedByModels);
                unsupportedModelBlocks = modelLoggingInfo.LogAsSpecifiedByModels(~rootModelMatchVector);
                % find additional Model block paths in Signal vector and add to
                % unsupportedModelBlocks; keep track of indices for filtering
                % later.
                numSigs = length(modelLoggingInfo.Signals);
                keepIndices = ones(1, numSigs);
                for sigInfoIdx = 1:numSigs
                    sigInfo = modelLoggingInfo.Signals(sigInfoIdx);
                    if sigInfo.BlockPath.getLength > 1
                        % multi-level model ref block path
                        keepIndices(sigInfoIdx) = 0;
                        unsupportedModelBlocks{end+1} = sigInfo.BlockPath.getBlock(1);  %#ok<AGROW>
                    end
                end
                % remove duplicates
                unsupportedModelBlocks = unique(unsupportedModelBlocks);
                % finally, if the root model is to be logged as specified in
                % model then create a new ModelLoggingInfo
                if modelLoggingInfo.getLogAsSpecifiedInModel(rootModel)
                    % get a ModelLoggingInfo for all signals in the rootModel
                    % ignore model references
                    ignoreRefModels = true;
                    modelLoggingInfo = coder.connectivity.SimulinkInterface.createLoggingInfoFromModel(rootModel, ...
                        ignoreRefModels);
                else
                    % remove Model block paths from the list that will be
                    % further processed
                    modelLoggingInfo.Signals = modelLoggingInfo.Signals(logical(keepIndices));
                end
            else
                assert((inTheLoopType == rtw.pil.InTheLoopType.ModelBlock) || ...
                    (inTheLoopType == rtw.pil.InTheLoopType.ModelBlockStandalone) || ...
                    (inTheLoopType == rtw.pil.InTheLoopType.RLSSubsystem), ...
                    'Expected inTheLoopType ModelBlock, ModelBlockStandalone or RLSSubsystem');                        
                  % Owing to incremental code generation, Data Logging
                  % information of the model is up to date.
                  %
                  % We only want to regenerate the wrapper if the set of signals
                  % marked for logging in the model changes (note that this
                  % may not trigger code generation).   The LoggingData
                  % checksum will trigger regeneration.
                  %
                  % Signal logging override changes do not regenerate the
                  % wrapper because we must support multiple instances of
                  % referenced models, each of which may have different
                  % signal logging configurations.    As an optimization, each
                  % instance of the wrapper s-function may ultimately
                  % dynamically choose which logged signals are
                  % currently active.
                  %
                  % get a ModelLoggingInfo for all signals in the rootModel
                  % ignore model references
                  ignoreRefModels = true;
                  modelLoggingInfo = coder.connectivity.SimulinkInterface.createLoggingInfoFromModel(rootModel, ...
                      ignoreRefModels);
                  unsupportedModelBlocks = {};
                  % also, determine the list of unsupported Model blocks that we
                  % must warn for
                  ignoreRefModels = false;
                  fullModelLoggingInfo = coder.connectivity.SimulinkInterface.createLoggingInfoFromModel(rootModel, ...
                      ignoreRefModels);
                  numSigs = length(fullModelLoggingInfo.Signals);
                  for sigInfoIdx = 1:numSigs
                      sigInfo = fullModelLoggingInfo.Signals(sigInfoIdx);
                      if sigInfo.BlockPath.getLength > 1
                          % multi-level model ref block path
                          unsupportedModelBlocks{end+1} = sigInfo.BlockPath.getBlock(1);  %#ok<AGROW>
                      end
                  end
                  % remove duplicates
                  unsupportedModelBlocks = unique(unsupportedModelBlocks);
            end                
        end  
        
        function loggingInfo = createLoggingInfoFromModel(rootModel, ignoreRefModels)
            % call Simulink.SimulationData.ModelLoggingInfo.createFromModel
            % and close referenced models that where loaded by that call
            
            if ignoreRefModels
                loggingInfo = Simulink.SimulationData.ModelLoggingInfo.createFromModel(rootModel, ...
                      'ReferencedModels', ...
                      'off');
            else
                openMdls = find_system('SearchDepth',0, 'type','block_diagram');
                loggingInfo = Simulink.SimulationData.ModelLoggingInfo.createFromModel(rootModel);
                openMdlsAfterLoad = find_system('SearchDepth',0, 'type','block_diagram');
                mdlsToClose = setdiff(openMdlsAfterLoad, openMdls);
                cellfun(@(x) close_system(x), mdlsToClose);
            end
        end
        
        function hasSignalHierarchyChildren = hasSignalHierarchyChildren(silPILWrapperUtils, ...
                                                                        sigInfo, ...
                                                                        inTheLoopType)
           % graphically check for virtual bus signals that are otherwise
           % undetectable owing to their code generation representation (see
           % g832576)
           %
           % Note: SignalHierarchy is only available when StrictBusMsg is set
           % appropriately, however, Top-Model SIL/PIL explicitly requires
           % this and so does "To Workspace" dataset logging for the Model
           % block which means an error is reported during the SIL/PIL 
           % SIM tgt build.   Therefore, we assert that StrictBusMsg is set to
           % ErrorLevel1 (or higher).
           
           hasSignalHierarchyChildren = false;
           
           % not valid for SIL/PIL block
           % when SILPILBlockSignalLogging is featured on we will need to
           % revisit this
           if inTheLoopType ~= rtw.pil.InTheLoopType.Block                      
               level = silPILWrapperUtils.getParam('StrictBusMsg');
               assert(~strcmp(level, 'None') && ...
                   ~strcmp(level, 'Warning'), ...
                   'SIL/PIL signal logging expects StrictBusMsg is always set to ErrorLevel1 or higher');
               assert(sigInfo.BlockPath.getLength == 1, ...
                   'Multi-level block paths should be filtered.');
               assert(isempty(sigInfo.BlockPath.SubPath), ...
                   'Stateflow SubPaths should be filtered.');
               blockPath = sigInfo.BlockPath.getBlock(1);
               portHandles = get_param(blockPath, 'PortHandles');
               portHandle = portHandles.Outport(sigInfo.OutputPortIndex);
               % get hierarchy
               signalHierarchy = get_param(portHandle, 'SignalHierarchy');
               % check for bus
               if  ~isempty(signalHierarchy) && ~isempty(signalHierarchy.Children)
                   hasSignalHierarchyChildren = true;
               end
           end
        end
        
        function [result, stateInfo, fatalWarn, nonFatalWarn] = processStateInfo(silPILWrapperUtils, ...
                inTheLoopType, dataInterface, dataInterfaceIdx, stateInfo)
            result = [];
            
            % unlike signal logging, the block path is computed from the
            % SID directly
            assert(~isempty(dataInterface.SID), 'The data interface must have a valid SID');
            bp = Simulink.BlockPath(Simulink.ID.getFullName(dataInterface.SID));
            sigInfo.BlockPath = bp;
            % output port is meaningful with state logging (the state
            % belongs to the entire block, not to an outport), a default
            % value is assigned
            sigInfo.OutputPortIndex = 1;
            
            % don't log states in triggered subsystems
            % this seems to work now - check UnsupportedConditionalContext
            supportConditionalSubsystems = coder.internal.connectivity.featureOn('CondSubSystemsLogging');
            [fatalWarn, nonFatalWarn, isExportFcnTrigger, isConditional] = coder.connectivity. ...
                SimulinkInterface.validateConditionalPorts(inTheLoopType, ...
                silPILWrapperUtils, sigInfo, supportConditionalSubsystems);
            if supportConditionalSubsystems
                stateInfo.postUpload = stateInfo.postUpload | isConditional;
            end
            if ~isempty(fatalWarn)
                return;
            end
            % check supported rates
            fatalWarn = coder.connectivity.SimulinkInterface.validateLoggingRates(...
                dataInterface, silPILWrapperUtils, sigInfo, isExportFcnTrigger);
            if ~isempty(fatalWarn)
                return;
            end
            % check buses - this can be skipped when g2411542 is done
            fatalWarn = coder.connectivity.SimulinkInterface.validateLoggingBusObjects(...
                dataInterface, silPILWrapperUtils, sigInfo, inTheLoopType);
            if ~isempty(fatalWarn)
                return;
            end
            % create state object
            loggingData = rtw.pil.StateLoggingData(bp, ...
                dataInterface, dataInterfaceIdx, ...
                coder.connectivity.SimulinkInterface, ...
                silPILWrapperUtils.getCodeInfoUtils, stateInfo.postUpload);
            loggingData.setNameMapping(stateInfo.signalNames);
            % validate data type: multi word types not supported
            fatalWarn = coder.connectivity.SimulinkInterface.validateLoggingDataType(...
                loggingData, dataInterface, sigInfo);
            if ~isempty(fatalWarn)
                return;
            end
            result = loggingData;
        end
        
        function [result, fatalWarn, nonFatalWarn] = processSigInfo(silPILWrapperUtils, ...
               sigInfo, ...
               dataInterface, ...
               dataInterfaceIdx, ...
               inTheLoopType, ...
               sigInfoSID, ...
               sigInfoName, ...
               busIndex, ...
               regionLength, ...
               regionOffset) 
            result = [];
            fatalWarn = [];
            nonFatalWarn = [];
            % Filter empty Implementations, e.g. custom storage
            % classes - this shouldn't be possible with code descriptor
            % because we discard data interfaces with empty implementation
            if isempty(dataInterface.Implementation)
                [id, message] = rtw.pil.ProductInfo.message('pil', ...
                    'UnsupportedSigLogImplementation');
                e = MException(id, message);
                fatalWarn.SignalInfo = sigInfo;
                fatalWarn.Exception = e;
                return;
            end
            % check buses - this can be skipped when g2411542 is done
            fatalWarn = coder.connectivity.SimulinkInterface.validateLoggingBusObjects(...
                dataInterface, silPILWrapperUtils, sigInfo, inTheLoopType);
            if ~isempty(fatalWarn)
                return;
            end
            % check for unsupported conditional contexts
            supportConditionalSubsystems = ...
                coder.internal.connectivity.featureOn('CondSubSystemsLogging');
            [fatalWarn, nonFatalWarn, isExportFcnTrigger] = coder.connectivity. ...
                SimulinkInterface.validateConditionalPorts(inTheLoopType, ...
                silPILWrapperUtils, sigInfo, supportConditionalSubsystems);
            if ~isempty(fatalWarn)
                return;
            end
            % unsupported sample time (e.g. triggered, continuous)
            fatalWarn = coder.connectivity.SimulinkInterface.validateLoggingRates(...
                dataInterface, silPILWrapperUtils, sigInfo, isExportFcnTrigger);
            if ~isempty(fatalWarn)
                return;
            end
            [sigInfoSID, sigInfo] = rtw.pil.LoggingUtils.updateSIDs(...
                silPILWrapperUtils, dataInterface, sigInfoSID, sigInfo);
            % create a potential LoggingData element            
            newLoggingData = rtw.pil.LoggingData(sigInfo, ...
                dataInterface, ...
                dataInterfaceIdx, ...
                coder.connectivity.SimulinkInterface, ...
                silPILWrapperUtils.getCodeInfoUtils, ...
                sigInfoSID, ...
                sigInfoName);
            newLoggingData.setRegionIndexes(busIndex, regionLength, regionOffset);
            try
                % validate data elements of newLoggingData
                dataElements = newLoggingData.getHostDataElements;
                result.LoggingIODatatypes = silPILWrapperUtils.validateDataElements(dataElements);
                result.LoggingData = newLoggingData;                
            catch e
                % validation failed
                fatalWarn.SignalInfo = sigInfo;
                fatalWarn.Exception = e;
                return;
            end
        end
        
        function warn = validateLoggingBusObjects(dataInterface, silPILWrapperUtils, sigInfo, inTheLoopType)
            warn = [];
            type = dataInterface.Type;
            isNVBus = rtw.connectivity.CodeInfoTypeUtils.isBusOrStruct(type);
            if (isNVBus && ~coder.internal.connectivity.featureOn('BusLogging')) || ...
                    (~isNVBus && coder.connectivity.SimulinkInterface.hasSignalHierarchyChildren(...
                    silPILWrapperUtils, sigInfo, inTheLoopType))
                % we still need to check for virtual buses (non-NVbus and
                % part of hierarchical signal) because virtual buses can be
                % expanded as output of modelref. So, we would get a signal
                % with a single DataInterface part of a virtual bus
                % (g2411542)
                [id, message] = rtw.pil.ProductInfo.message('pil', 'UnsupportedSigLogBusSignal');
                e = MException(id, message);
                warn.SignalInfo = sigInfo;
                warn.Exception = e;
            end       
        end
        
        function warn = validateLoggingRates(dataInterface, silPILWrapperUtils, sigInfo, isExportFcnTrigger)
            warn = [];
            
            % unsupported sample time (e.g. triggered)
            if isempty(dataInterface.Timing)
                [id, message] = rtw.pil.ProductInfo.message('pil', ...
                    'UnsupportedSigLogTimingInterface');
                e = MException(id, message);
                warn.SignalInfo = sigInfo;
                warn.Exception = e;
                % skip to next signal
                return;
            end
            % unsupported rates (e.g. continuous)
            timingMode = dataInterface.Timing.TimingMode;
            allowedRates = {'PERIODIC', 'ONESHOT', 'INHERITED'};
            if isExportFcnTrigger
                % allow async for export functions only
                allowedRates{end+1} = 'ASYNCHRONOUS';
            end
            if silPILWrapperUtils.getCodeInfoUtils.UseMF0Logging && ...
                    silPILWrapperUtils.getCodeInfoUtils.hasModelWideEvents
                allowedRates{end+1} = 'TERMINATE';
                allowedRates{end+1} = 'INITIALIZE';
                allowedRates{end+1} = 'RESET';
                allowedRates{end+1} = 'RESETWITHINIT';
            end
            if ~any(strcmp(timingMode, allowedRates))
                [id, message] = rtw.pil.ProductInfo.message('pil', ...
                    'UnsupportedSigLogTimingMode', lower(char(timingMode)));
                e = MException(id, message);
                warn.SignalInfo = sigInfo;
                warn.Exception = e;
            end
        end
        
        function warn = validateLoggingDataType(loggingData, dataInterface, sigInfo)
            warn = [];
            
            dataElements = loggingData.getHostDataElements;
            for i=1:length(dataElements)
                if isempty(dataElements(i).getDtypeId)
                    % the type is not supported: for example, it is a
                    % multiword type (e.g., sfix74_En3)
                    if rtw.connectivity.CodeInfoUtils.isa(dataInterface.Implementation, 'Variable')
                        identifier = dataInterface.Implementation.Identifier;
                    else
                        identifier = dataInterface.Implementation.ElementIdentifier;
                    end
                    [id, message] = rtw.pil.ProductInfo.message('pil', ...
                        'UnsupportedInterfaceType', identifier);
                    e = MException(id, message);
                    warn.SignalInfo = sigInfo;
                    warn.Exception = e;
                    return;
                end
            end
        end
        
        function [fatalWarn, nonFatalWarn, isExportFcnTrigger, isConditional] = ...
                validateConditionalPorts(inTheLoopType, silPILWrapperUtils, ...
                    sigInfo, supportConditionalSubsystems)
            fatalWarn = [];
            nonFatalWarn = [];
            isExportFcnTrigger = false;
            isConditional = false;
            
            if inTheLoopType == rtw.pil.InTheLoopType.Block
               return;
            end
            
            assert(sigInfo.BlockPath.getLength == 1, 'Multi-level block paths should be filtered.');
            assert(isempty(sigInfo.BlockPath.SubPath), 'Stateflow SubPaths should be filtered.');
            blockPath = sigInfo.BlockPath.getBlock(1);
            % look at parent subsystem
            parentSS = get_param(blockPath, 'Parent');
            [condPorts, triggerPorts, enablePorts, iterPorts, actionPorts] = ...
                coder.internal.utils.findUnsupportedConditionalPorts(parentSS);
            
            isConditional = ~isempty(condPorts);
            
            if ~isConditional
                % no conditional port, no warning to generate
                return;
            end
            
            if length(condPorts) ~= 1
                % if there are more than one port, throw the generic fatal
                % warning
                fatalWarn = coder.connectivity.SimulinkInterface.generateConditionalPortWarning(condPorts, 'UnsupportedConditionalContext', sigInfo);
                return;
            end
            
            if supportConditionalSubsystems
                if ~isempty(enablePorts)
                    % manage enabled subsystems
                    nonFatalWarn = coder.connectivity.SimulinkInterface. ...
                        generateConditionalPortWarning(condPorts, 'SupportedWithLimitationsEnabledSubsystems', sigInfo);
                    return;
                end
                
                if ~isempty(iterPorts)
                    % manage for- and while-iterator subsystems
                    nonFatalWarn = coder.connectivity.SimulinkInterface. ...
                        generateConditionalPortWarning(condPorts, 'SupportedWithLimitationsForWhileIterators', sigInfo);
                    return;
                end
                
                if ~isempty(actionPorts)
                    isConditional = false;
                    nonFatalWarn = coder.connectivity.SimulinkInterface. ...
                        generateConditionalPortWarning(condPorts, 'SupportedWithLimitationsActionSubsystems', sigInfo);
                    return;
                end
            end
            
            % Allow:
            % - Model block: Top-level fcn-call trigger.
            % - Export functions fcn-call trigger.
            if ~isempty(triggerPorts)
                trigPort = triggerPorts{1};
                triggerType = get_param(trigPort, 'TriggerType');
                isFcnCall = strcmpi(triggerType, 'function-call');
                % filter Simulink Functions because we do not yet
                % process / upload logging data for them
                isSimulinkFunction = strcmp(get_param(trigPort, 'IsSimulinkFunction'), 'on');
                if ~isSimulinkFunction
                    if isFcnCall
                        % could be fcn-call subsystem or root model
                        fcnCallParent = get_param(trigPort, 'Parent');
                        % Grandparent must be the root model or empty. If
                        % not, the fcnCall is within one or more subsystem:
                        % go up until the parent is either empty or the root
                        fcnCallGrandparent = get_param(fcnCallParent, 'Parent');
                        lRootModel = bdroot(trigPort);
                        while ~isempty(fcnCallGrandparent) && ...
                                ~strcmp(lRootModel, fcnCallGrandparent) && ...
                                strcmp(get_param(fcnCallGrandparent, 'blocktype'), 'SubSystem')
                            fcnCallGrandparent = get_param(fcnCallGrandparent, 'parent');
                        end
                        if isempty(fcnCallGrandparent) && ...
                                (inTheLoopType == rtw.pil.InTheLoopType.ModelBlock)
                            % Model block XIL:
                            % root level periodic or trigger fcn-call is ok
                            return;
                        elseif strcmp(fcnCallGrandparent, lRootModel) && ...
                                silPILWrapperUtils.getCodeInfoUtils.isExportFunctions
                            % Export functions:
                            % parent subsystem of fcn-call must be at the root level
                            % periodic or trigger fcn-call is ok
                            if silPILWrapperUtils.getCodeInfoUtils.isExportFcnDiagram
                                % this corresponds to a root entry point
                                isExportFcnTrigger = true;
                                return;
                            else
                                % check for connection to Async Task Specification block to
                                % make sure we're dealing only with the root
                                % entry points
                                portConnectivity = get_param(fcnCallParent, 'PortConnectivity');
                                for pcIdx=1:length(portConnectivity)
                                    pc = portConnectivity(pcIdx);
                                    if strcmp(pc.Type, 'trigger')
                                        if ~isempty(pc.SrcBlock) && strcmp(get_param(pc.SrcBlock, 'BlockType'), 'AsynchronousTaskSpecification')
                                            isExportFcnTrigger = true;
                                            return;
                                        end
                                        break;
                                    end
                                end
                            end
                        elseif supportConditionalSubsystems
                            portConnectivity = get_param(fcnCallParent, 'PortConnectivity');
                            for pcIdx=1:length(portConnectivity)
                                pc = portConnectivity(pcIdx);
                                blockType = get_param(pc.SrcBlock, 'blocktype');
                                if ~isempty(blockType) && strcmp(blockType, 'S-Function') && strcmp(get_param(pc.SrcBlock, 'mask'), 'on')
                                    mask = Simulink.Mask.get(pc.SrcBlock);
                                    if strcmp(mask.Type, 'Function-Call Generator') && strcmp(get_param(pc.SrcBlock, 'numberOfIterations'), '1')
                                        return;
                                    end
                                end
                            end
                        end
                    end
                    if supportConditionalSubsystems
                        % triggered subsystems or function-call subsystems whose
                        % caller is either a Stateflow chart or a function-call
                        % generator with numberOfIterations greater than 1
                        nonFatalWarn = coder.connectivity.SimulinkInterface. ...
                            generateConditionalPortWarning(condPorts, ...
                            'SupportedWithLimitationsTriggeredSubsystems', sigInfo);
                        return;
                    end
                end
            end
            
            % catch triggered (simulink function) and action subsystems
            fatalWarn = coder.connectivity.SimulinkInterface. ...
                    generateConditionalPortWarning(condPorts, 'UnsupportedConditionalContext', sigInfo);
        end
        
        function warn = generateConditionalPortWarning(conditionalPorts, messageId, sigInfo)
            warn = [];
            
            if ~isempty(conditionalPorts)
                condPortStr = '';
                for condPortIdx=1:length(conditionalPorts)
                    currCondPort = conditionalPorts{condPortIdx};
                    % remove newlines by replacing with spaces which are
                    % handled the same by Simulink
                    currCondPort = strrep(currCondPort, newline, ' ');
                    % hilite the unsupported block via a hyperlink
                    link = coder.connectivity.SimulinkInterface.createHyperlinkToUnsupportedBlock(currCondPort);
                    if condPortIdx ~= length(conditionalPorts)
                        endStr = ', ';
                    else
                        endStr = '';
                    end
                    condPortStr = [condPortStr link endStr]; %#ok<AGROW>
                end
                [id, message] = rtw.pil.ProductInfo.message('pil', messageId, condPortStr);
                e = MException(id, message);
                warn.SignalInfo = sigInfo;
                warn.Exception = e;
            end
        end
    end
end
