classdef (Hidden = true) SubsystemTargetInterface < rtw.pil.ModelBlockTargetInterface   
%SUBSYSTEMTARGETINTERFACE creates a subsystem target interface file
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2019-2023 The MathWorks, Inc.

    properties(Access = 'private')
        NeedsNonFiniteSetup(1,1) logical = false;
    end
   
    methods (Access = 'protected')
        function checkIOReadOnly(this, dataInterface, dataInterfaceType)
            % Check data interfaces are not read-only. This method
            % overrides the parent class method.
            arguments
                this
                dataInterface coder.descriptor.DataInterface {mustBeScalarOrEmpty}
                dataInterfaceType char {mustBeMember(dataInterfaceType, {'inport', 'outport', 'datastore'})}
            end

            if isa(dataInterface.Implementation, 'coder.descriptor.TypedCollection')
                % For typed collections we need to loop over all
                % implementations
                implList = rtw.connectivity.CodeInfoUtils.getArray(dataInterface.Implementation.Elements);
            else
                implList = dataInterface.Implementation;
            end

            for i = 1:length(implList)
                owner = this.getImplementationOwner(implList(i));
                % In the case of RLS workflows, if SIL takes ownership
                % of a read-only variable, SIL defines its own writable
                % (non-constant) storage for syncing the data interface.
                % In this case we need to relax the check, as SIL will
                % not write to any constant memory sections (g2983170).
                if isempty(owner) || strcmp(owner, 'PIL')
                    continue;
                else
                    % Validate the dataInterface readability via superclass
                    checkIOReadOnly@rtw.pil.TargetInterface( ...
                        this, dataInterface, dataInterfaceType);
                end
            end
        end

        function absTimeData = getAbsoluteTimeData(this)
            % Override the superclass method with a default implementation, 
            % i.e. no ClockTicks required.
            % The ModelInterface for RLS is generated from top-level code 
            % and does not contain ClockTicks info. This is only relevant 
            % if timingBridge is required and that applies only to model 
            % reference code.
            
            if this.SILPILWrapperUtils.getSILPILInterface().getInTheLoopType() == rtw.pil.InTheLoopType.ModelBlock
                absTimeData = getAbsoluteTimeData@rtw.pil.ModelBlockTargetInterface(this);
            else
                
                modelInterface = this.SILPILWrapperUtils.getInfoStruct.modelInterface;
                
                assert(~(isfield(modelInterface, 'ClockTicksReq') || ...
                    isfield(modelInterface, 'LongClockTicksReq')), ...
                    'ClockTicksReq and LongClockTicksReq are not expected in modelInterface for RLS');
                
                absTimeData.XILWrapperProvideClockTicks = false;
                
                absTimeData.ClockTicksReqVector = 0;
                absTimeData.LongClockTicksReqVector = 0;
                
                absTimeData.isClockTicksRequired = false;
                absTimeData.isLongClockTicksRequired = false;
            end
        end  
        
        function isObj = isCPPModelObject(~, ~)
            % Subsystem code is not a class, there is no corresponding
            % ModelObject.
            isObj = false;
        end
        
        function modelObject = getCPPModelObjectImpl(~) %#ok<STOUT>
            assert(false, 'getCPPModelObjectImpl should not be called for Subsystem code');
        end
        
        function isCAPIEnabled = isCAPIEnabled(~)
            isCAPIEnabled = false;
        end
        
        function writeSectionIncludes(this)
            % call super class method
            writeSectionIncludes@rtw.pil.ModelBlockTargetInterface(this); 
            
            if this.codeInfoUtils.getCodeInfo().UsesNonFiniteUtils
               % add include to non-finites declarations
               this.writer.writeLine('#include "rt_nonfinite.h"'); 
               this.writer.newLine;
            end
        end 
        
        function writeSectionInitBody(this)            
            
            % initialize Inf and Nan
            langStd = this.SILPILWrapperUtils.getParam('TargetLangStandard');
            useNonfiniteLiterals = slfeature('SupportNonfiniteLiterals') && ...
                ~(strcmp(langStd, 'C89/C90 (ANSI)') || ...
                  strcmp(langStd, 'C++03 (ISO)'));
            ssInterface = this.codeInfoUtils.getCodeInfo();
            if ssInterface.UsesNonFiniteUtils && ~useNonfiniteLiterals
               this.writer.wComment('initialize non-finites');
               this.writer.writeLine('rt_InitInfAndNaN(sizeof(real_T));'); 
               this.writer.newLine;
            end
            
            % initialize Zero Crossing Events
            zcEvents = ssInterface.ZeroCrossingEvents;
            numZCEs = length(zcEvents);
            if numZCEs > 0
                this.writer.wComment('initialize zero crossing events');
                initialValues = this.codeInfoUtils.ZCEInitialValues;
                this.writer.wBlockStart('');
                for idx = 1:numZCEs
                    % the implementation should be a StructExpression, use
                    % directly getExpression
                    this.writer.writeLine('%s = %s;', ...
                        zcEvents(idx).Implementation.getExpression(), ...
                        initialValues{idx});
                    this.writer.newLine;
                end
                this.writer.wBlockEnd(); % empty block start
            end
            
            % call super class method
            writeSectionInitBody@rtw.pil.ModelBlockTargetInterface(this);
        end

        function validateSDPServices(this)
            % validate SDP service support            
            pluginManager = this.SILPILWrapperUtils.getSILPILInterface().getServicePluginManager();
            if pluginManager.HasActiveServices && pluginManager.IsServiceInterfaceActive
                exception = MSLException('PIL:pil_subsystem:UnsupportedSDPServices', ...
                    this.SILPILWrapperUtils.getComponentForDiagnostics);
                exception.throw;
            end          
        end
    end
end
