classdef (Hidden = true) StandaloneTargetInterface < rtw.pil.TargetInterface   
%STANDALONETARGETINTERFACE creates a standalone target interface file
%
%   STANDALONETARGETINTERFACE creates a standalone target interface file defined
%   by CodeInfo
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2008-2022 The MathWorks, Inc.
        
    
    methods (Access = 'protected')       
        function writeSectionStorageHook(this)                      
            % allocate storage for internal data
            this.writeSectionStorageInternalData;
        end
        
        function writeSectionDefinesMatFileLogging(this)
            this.writer.wLine('/* Matfile logging */')
            this.writer.wLine('#define QUOTE1(name) #name');
            this.writer.wLine('#define QUOTE(name) QUOTE1(name)    /* need to expand name    */');
            this.writer.wLine('#ifndef SAVEFILE');
            this.writer.wLine('# define MATFILE2(file) #file ".mat"');
            this.writer.wLine('# define MATFILE1(file) MATFILE2(file)');
            this.writer.wLine('# define MATFILE MATFILE1(MODEL)');
            this.writer.wLine('#else');
            this.writer.wLine('# define MATFILE QUOTE(SAVEFILE)');
            this.writer.wLine('#endif');
            this.writer.wNewLine;
        end
        
        function writeSectionOutputBodyMatFileLogging(this)            
            this.writer.writeLine('/* Matfile logging */');
            RTMLoggingObjectExpression =...
                this.SILPILWrapperUtils.getInfoStruct.GeneralDataFromTLC.RTMLoggingObjectExpression;
            RTMTPtrExpression =...
                this.SILPILWrapperUtils.getInfoStruct.GeneralDataFromTLC.RTMTPtrExpression;            
            this.writer.wLine(['rt_UpdateTXYLogVars('  RTMLoggingObjectExpression ', ' RTMTPtrExpression ');']);                        
        end
        
        function writeSectionTerminateMatFileLogging(this)
            RTMLoggingObjectExpression = this.getRealLoggingName();
            this.writer.wLine('   /* Matfile logging */');
            this.writer.wLine(['   rt_StopDataLogging(MATFILE, '  RTMLoggingObjectExpression ');']);            
        end
        
        function validateInterface(this)
            % call super class method            
            validateInterface@rtw.pil.TargetInterface(this);            
            % check external mode is not switched on, otherwise we would
            % get link errors            
            if strcmp(this.SILPILWrapperUtils.getParam('ExtMode'), 'on')
                rtw.pil.ProductInfo.error('pil', 'StandaloneExtMode', ...
                    this.codeInfo.GraphicalPath);
            end                    
        end
        
        function validateParamDataInterface(this, dataInterface)            
            % Validation to make sure we can initialize parameters
            % correctly:
            %
            % The main unsupported case is imported "rtP" parameters
            % (i.e. reusable code interface) that aren't supported for
            % dynamic initialization.   To avoid the need for the user
            % to have to define / initialize the rtP struct we have never
            % allowed this.   If SIL/PIL can dynamically initialize all
            % "rtP" members then we allow it.
            %
            % Other imported parameters that aren't supported for dynamic
            % initialization will be left undefined for the user to
            % define and initialize.   One caveat with this is imported
            % struct parameters where only some of the fields are supported
            % for dynamic initialization.   In that case, SIL/PIL will
            % define the struct and support initialization / tuning of
            % the supported parameters.   The unsupported parameters
            % will be zero initialized.
            %
            
            this.InterfaceStrategy.validateParamDataInterfaceHook(dataInterface);
            
            implementation = dataInterface.Implementation;
            if this.isRtPMember(dataInterface) && ~this.isDefined(implementation)                
                isAllocFcn = strcmp(this.SILPILWrapperUtils.getParam('GenerateAllocFcn'), 'on');
                if isAllocFcn
                    % allocation function will define rtP
                    assert(this.codeInfoUtils.isa(dataInterface.Implementation, 'StructExpression'));
                    % setting an owner up front means that rtP gets skipped
                    % during storage processing
                    this.setImplementationOwner(dataInterface.Implementation.BaseRegion, 'PIL');
                else                          
                    if ~this.SILPILWrapperUtils.isSupportedTunableWorkspaceParam(dataInterface) && ...
                            ~this.SILPILWrapperUtils.isCPPEncap
                        % no support for non-allocation function scenario
                        % for unsupported workspace params, except for C++
                        % class generation which initializes auto
                        % parameters via the class constructor 
                        
                        assert(strcmp(this.SILPILWrapperUtils.getParam('MultiInstanceERTCode'), 'on'), ...
                            'rtP found when not generating reusable code.');
                                                                        
                        diString = this.codeInfoUtils.getDataInterfaceString(dataInterface, 'Parameter');
                        if this.codeInfoUtils.isWorkspaceParam(dataInterface)
                            rtw.pil.ProductInfo.error('pil', ...
                                'UndefinedParameter', ...
                                diString);
                        else
                            rtw.pil.ProductInfo.error('pil', ...
                                'NonWorkspaceParamWithReusableCode', ...
                                diString);
                        end
                    end
                end
            end
        end
        
        function validateInstSpecificParameters(this)
            % The main use case for this check is to detect if the
            % generated code contains instance specific parameters that
            % SIL/PIL cannot initialize correctly.
            % In particular, top-level reusable-code does not initialize
            % the instance specific parameters that belong to its Model
            % blocks (grouped model arguments of referenced models). 
            % See g1731462.
            
            IsMultiInstance =  strcmp(this.SILPILWrapperUtils.getParam(...
                'CodeInterfacePackaging'), 'Reusable function');
            groupsInfo = this.SILPILWrapperUtils.getCodeInfoUtils.CoderGroupsInfo;
            % if is top-level, multi-instance code with coder groups
            if IsMultiInstance && ...
                    groupsInfo.ProcessGroups && ...
                    this.SILPILWrapperUtils.getSILPILInterface.isTopModelXILSim
                groupsWithModelArgs = groupsInfo.Groups( ...
                    [groupsInfo.Groups.ContainsInstanceSpecificParameter]);
                % if we have grouped model arguments 
                if ~isempty(groupsWithModelArgs)
                    % if any, the structs containing grouped model args of 
                    % referenced models are represented in the top-level
                    % InternalData
                    dataInterfaces = this.codeInfoUtils.getCodeInfo.InternalData;
                    % get the groups RootTypeName
                    groupTypes = arrayfun(@(x)(x.RootTypeName), ...
                        groupsWithModelArgs, 'UniformOutput', false);
                    % match the RootTypeName with the data interface type
                    % identifier to find any grouped arguments
                    numDataInterfaces = numel(dataInterfaces);
                    for interf_idx = 1:numDataInterfaces
                        typeID = rtw.connectivity.CodeInfoUtils.getTypeIdentifier(...
                            dataInterfaces(interf_idx));
                        if ismember({typeID}, groupTypes)
                            rtw.pil.ProductInfo.error('pil', 'UninitializedInstPArgs', ...
                                this.SILPILWrapperUtils.getComponentForDiagnostics());
                        end
                    end
                end 
            end
        end
        
        % If an Alloc function exists, and interface is internal data then
        % the alloc function will dynamically create the memory for pointer
        % variables.       
        function needToAllocate = needToAllocateHook(this, dataInterface, isPointerVariable)
            
            needToAllocate = true;
            
            allocFunction = this.codeInfo.AllocationFunction;
            if ~isempty(allocFunction) && isPointerVariable
                if isequal(dataInterface,allocFunction.ActualReturn)
                    
                    %If this is an internal Data item, and there is an
                    %Alloc function
                    needToAllocate = false;
                end
            end
        end
        
    end   
    
    methods (Access = 'private')          
       function rtP = getRtP(this)
           % search for "Parameter" rtP structure entry in the InternalData if it exists
           % otherwise return empty 
           rtP = [];
           data = this.codeInfo.InternalData;
           for i=1:length(data)
               currData = data(i);               
               if strcmp('Parameter', currData.GraphicalName)                   
                   rtP = currData;
                   break;
               end
           end
       end
       
       function isRtPMember = isRtPMember(this, dataInterface)
           % check to see if a dataInterface is part of the rtP structure
           rtP = this.getRtP;
           if isempty(rtP)
               isRtPMember = false;
           else
               storageImplementation = this.getBaseStorageImplementation(dataInterface.Implementation);
               % Case (storageImplementation == rtP.Implementation): 
               %      E.g. non-reusable code
               % Case (isa(rtP.Implementation,'RTW.PointerExpression') && ...
               %      (storageImplementation==rtP.Implementation.TargetRegion)): 
               %      E.g. reusable code
               if ~isempty(storageImplementation) && ...
                       ((storageImplementation == rtP.Implementation) || ...
                       (this.codeInfoUtils.isa(rtP.Implementation,'PointerExpression') && ...
                       (storageImplementation==rtP.Implementation.TargetRegion)))
                   isRtPMember = true;
               else
                   isRtPMember = false;
               end
           end
       end
       
       function loggingObject = getRealLoggingName(this)
           % modify the logging name if the target language is
           % C++ (Encapsulated) to objectName.loggingFcn
           RTMLoggingObjectExpression =...
                this.SILPILWrapperUtils.getInfoStruct.GeneralDataFromTLC.RTMLoggingObjectExpression; 
                    
            if this.SILPILWrapperUtils.isCPPEncap
               RTMLoggingObjectFcn = regexp(RTMLoggingObjectExpression,'\->(\S+)','tokens','once');
               modelObjectImpl = this.getCPPModelObjectImpl();
               RTMLoggingObject    = modelObjectImpl.Identifier;
               RTMLoggingObjectExpression = sprintf('%s.getRTM()->%s',...
                    RTMLoggingObject,...
                    RTMLoggingObjectFcn{1});
           end
            
           loggingObject = RTMLoggingObjectExpression;
       end
    end
end
