classdef (Hidden = true) ModelRefSILPILWrapperUtils < rtw.pil.SILPILWrapperUtils
%MODELREFSILPILWRAPPERUTILS SIL/PIL model reference code interface utilities
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2010-2020 The MathWorks, Inc.
        
    methods
        % constructor
        function this = ModelRefSILPILWrapperUtils(silPILInterface, ...
                                            infoStruct, ...
                                            clientInterface,...
                                            topModelOriginalParams, ...
                                            lXilCompInfo)

            % call super class constructor                             
            this@rtw.pil.SILPILWrapperUtils(silPILInterface, ...
                                            infoStruct, ...
                                            clientInterface, ...
                                            topModelOriginalParams, ...
                                            lXilCompInfo);
                                                    
            if this.isModelReferenceTarget && ...
                    ~silPILInterface.IsUpdateModelReferenceTargets
                        % Don't throw error if just updating RTW targets
                        % for model ref propagation.  This code path will
                        % be visited again during XILBuildHook for the top
                        % level of a SIL/PIL hierarchy.
               if strcmp(this.getParam('SupportVariableSizeSignals'), 'on')
                   % so long as sizes of variable-size signals are propagated
                   % during execution we will support variable size signals
                   if ~strcmp(this.getParam('PropagateVarSize'), 'During execution')                    
                       DAStudio.error('PIL:pil:ModelBlockVariableSizeSignals', ...
                                       this.getComponentForDiagnostics, ...
                                       this.getParam('PropagateVarSize'));                    
                   end
               end
            end
            
            info = this.getSILPILInterface.getInfoStruct.modelInterface;
            
            
            % Check that the model has inports
            if ~isfield(info,'Inports')          
                this.InportDirectFeedThrough = [];
            else            
                % Filter out Function call Inports. 
                % info.Inports can be a single matrix or a cellarray,
                % depending by its cardinality 
                if isscalar(info.Inports)
                    inports{1} = info.Inports;
                else
                    inports = info.Inports;
                end                
                
                noFcn = @(y)~inports{y}.IsFcnCall; %Filter out function call inputs
                
                localInports = inports(arrayfun(noFcn,1:numel(info.Inports) ));
                
                for i = 1:numel(localInports)
                    this.InportDirectFeedThrough(i) = localInports{i}.DirectFeedThrough;
                end
            end
            
            
        end       
        
        function runDeferredValidation(this)
            % Deferred validation to be run only before attempting to build
            % the XIL application
            if this.isModelReferenceTarget && ...
                    ~this.getSILPILInterface.IsUpdateModelReferenceTargets
                % continuous time is not supported at all for Model Block SIL/PIL
                % owing to solver complexities
                if (strcmp(this.getParam('SupportContinuousTime'), 'on') && ~isempty(this.CodeInfoUtils.getCodeInfo.DerivativeFunction))
                    rtw.pil.ProductInfo.error('pil', 'ModelBlockContinuousTime', ...
                        this.getComponentForDiagnostics);
                end
                
                % constant outputs are not currently supported for Model Block
                % SIL/PIL owing to them being initialized by top model code rather
                % than the model reference code itself.
                %
                % This check should not be carried out when updating model
                % reference targets, because constant outputs are not a
                % problem at deeper levels of the hierarchy, since the
                % ownership then resides entirely within the XIL application.
                %
                % It will be run by the build hook when a simulation attempt
                % is made.
                if this.CodeInfoUtils.hasConstOutput
                    rtw.pil.ProductInfo.error('pil', 'ModelBlockConstOutputs', ...
                        this.getComponentForDiagnostics);
                end
            end
        end

        % return whether we should obfuscate the SIL/PIL wrapper files
        function obfuscateCode = shouldObfuscateXILWrapperFiles(this)
        % obfuscate the SIL/PIL wrapper files if the referenced model is protected
        % and if its contents is either obfuscated or binaries.
            obfuscateCode = false;
            if this.isModelProtected
                opts = Simulink.ModelReference.ProtectedModel.getOptions(...
                    this.SILPILInterface.ProtectedModelFullName);                
                obfuscateCode = opts.obfuscateCode || opts.binariesAndHeadersOnly;
            end
        end
        
        % Return the top model.
        function instrumentationSettingsModel = getInstrumentationSettingsModel(this)                    
            isTopModelXILSim = this.getSILPILInterface.isTopModelXILSim;
            if isTopModelXILSim
                instrumentationSettingsModel = this.getComponentName;
            else
                instrumentationSettingsModel = this.SILPILInterface.getTopModelForModelBlockPIL;        
            end
        end  
        
        % accessor for Top-Model ConfigSet params
        function value = getInstrumentationSettingsModelParam(this, param)                                   
           % assume model is open
           if ~isempty(this.TopModelOriginalParams) && this.TopModelOriginalParams.isKey(param)
               value = this.TopModelOriginalParams(param);
           else
               value = get_param(this.getInstrumentationSettingsModel, param);
           end
        end  
        
        function dataStoresToTransfer = getDataStoresToTransferIndices(this, allIndices, dsmType)
            % for backwards compatibility
            % (e.g. tSharedLocalDSM: verifyFileSSCSIL (File Scope)), limit
            % what we transfer when the Model block is used for top-model
            % SIL/PIL
            if this.getSILPILInterface.isTopModelXILSim
                dataStoresToTransfer = ...
                    getDataStoresToTransferIndices@rtw.pil.SILPILWrapperUtils(...
                        this, ...
                        allIndices, ...
                        dsmType); 
            else
                dataStoresToTransfer = allIndices;
            end
        end
        
        function dataStoresToTransfer = getDataStoresToTransferInterfaces(this, allInterfaces)            
            % for backwards compatibility
            % (e.g. tSharedLocalDSM: verifyFileSSCSIL (File Scope)), limit
            % what we transfer when the Model block is used for top-model
            % SIL/PIL
            if this.getSILPILInterface.isTopModelXILSim
                dataStoresToTransfer = ...
                    getDataStoresToTransferInterfaces@rtw.pil.SILPILWrapperUtils(...
                        this, ...
                        allInterfaces);                        
            else
                dataStoresToTransfer = allInterfaces;
            end
        end
    end     
    
    methods (Access = 'protected')        
        
        % Does this flavor support tunable workspace parameters?
        function supports = supportsModelWorkspaceParams(this)
            if this.getSILPILInterface.isTopModelXILSim
                % Top-model SIL/PIL supports this by registering these
                % parameters as S-Function parameters on the Model block's
                % hidden S-Function.   The values come from the duplicated
                % model workspace in the harness model.
                supports = true;
            else
                supports = false;
            end
        end
        
        function matFileName = getCAPIMatFileName(this)
            if this.isModelReferenceTarget 
               matFileName = 'capi_mdlref.mat';
            else
               matFileName = 'capi.mat';
            end
        end                                           
                                                      
        function warnForUnsupportedTunableParameters = warnForUnsupportedTunableParameters(this)            
            if this.getSILPILInterface.isTopModelXILSim
                % for backwards compatibility with classic top-model 
                % SIL/PIL, warn.
                warnForUnsupportedTunableParameters = true; 
            else
                % error
                warnForUnsupportedTunableParameters = false;
            end
        end
        
        % validate non-workspace parameters
        function paramIODatatypes = validateTunableNonWorkspaceParametersHook(this)            
            if this.isModelReferenceTarget 
                % process model args
                maIndices = this.CodeInfoUtils.getModelArgParams;
                maParams = this.getCodeInfo.Parameters(maIndices);
                % assert model arguments parameters are always accessible and writable
                isReadOnly = this.CodeInfoUtils.isDataInterfaceReadOnly(maParams);
                if any(isReadOnly)
                    assert(false, 'Found readOnly Model Argument');
                end
                isStatic = this.CodeInfoUtils.isDataInterfaceStatic(maParams);
                if any(isStatic)
                    assert(false, 'Found static Model Argument');
                end
                dataElements = rtw.pil.CodeInfoData.createHostDataElements(this, maIndices, 'parameter');
                paramIODatatypes = this.validateDataElements(dataElements);
            else
                paramIODatatypes = [];
            end
        end                 
        
        function validateIndividualPIMMapping(this)
            % detect individual mapping of 'internals' to AUTOSAR PIM and
            % error out
            if this.isModelReferenceTarget && strcmp(this.getParam('AutosarCompliant'), 'on')
                for idx = 1:numel(this.getCodeInfoUtils.CodeDescriptorInternals)
                    impl = this.getCodeInfoUtils.CodeDescriptorInternals(idx).Implementation;
                    if ~isempty(impl) && ...
                            (isa(impl, 'coder.descriptor.AutosarMemoryExpression')) && ...
                            strcmp(impl.DataAccessMode, 'ArTypedPerInstanceMemory')
                        rtw.pil.ProductInfo.error('pil', 'UnsupportedIndividualPIMMapping');
                    end
                end
            end
        end
        
        function validateModelArguments(this)
            
            if ~this.getSILPILInterface.isTopModelXILSim && ...
                    ~this.isModelReferenceTarget
                % Standalone code generation fully supports instance
                % specific (grouped) Model Arguments.    Non-instance
                % specific Model Arguments become inlined in the generated
                % code.    This is ok for top-model SIL/PIL where the use
                % of the Model block is hidden in the wrapper model.    For
                % regular Model block SIL/PIL, to guarantee to match 
                % simulation behavior across different simulation modes we
                % error out.
                
                % Cell array of model args in MF0. For top-level code MF0
                % contains only instance specific parameters
                mf0 = this.MF0ModelInfo;
                modelArgs = mf0.interfaceParameterInfo.modelArguments.toArray;
                modelArgsInMF0 = arrayfun(@(x)x.parameterName, modelArgs, 'UniformOutput', false);
                
                % use serialized ParameterArgumentNames in binfo.
                parameterArgumentNames = this.getInfoStruct.ParameterArgumentNames;
                tokens = regexp(parameterArgumentNames, '(\w*),?', 'tokens');
                % cell array of model args in the block UI
                if isempty(tokens)
                    modelArgsInUI = tokens;
                else
                    modelArgsInUI = [tokens{:}];
                end
                
                % if the UI contains more arguments than MF0, i.e. there is
                % at least one non-instance-specific argument
                isInUIAndMF0 = ismember(modelArgsInUI, modelArgsInMF0);
                if ~all(isInUIAndMF0)
                    argumentNames = strjoin(modelArgsInUI(~isInUIAndMF0), ', ');
                    this.CodeInfoUtils.throwUnsupportedModelArgsError(...
                        this.getComponentForDiagnostics, argumentNames);
                end
            end
        end
    end          
end
