classdef (Hidden = true) SubsystemSfunction < rtw.pil.ModelBlockSfunction
%SubsystemSfunction creates an s-function for Subsystem SIL/PIL
%
% 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.

methods
% constructor
function this = SubsystemSfunction(silPILWrapperUtils, ...
sfunSourcePath, ...
sfunOutDir)
narginchk(3, 3);

% call super class constructor
this@rtw.pil.ModelBlockSfunction(silPILWrapperUtils, ...
sfunSourcePath, ...
sfunOutDir);
end
end

methods(Access = protected)

function validateInterface(this)
validateInterface@rtw.pil.ModelBlockSfunction(this);
this.validateNumIOPortsAgainstSourceComponent();
end

function doProcess = needToProcessEnableDisableFcn(~)
% always process Enable/Disable functions
doProcess = true;
end

function validateNumIOPortsAgainstSourceComponent(this)
% check the number of inports and outports in code descriptor
% matches up with the number of inports and outports in the
% subsystem graphical interface
% NOTE: We should look to move this check earlier, but this is
% a safe place for now.
errorId = 'PIL:pil_subsystem:NumIOPortMismatch';

mF0 = this.SILPILWrapperUtils.MF0ModelInfo;
% skip function call inports that are not described in code
% descriptor
mF0Inports = mF0.inports.toArray();
mF0DataInports = mF0Inports(~[mF0Inports.isFunctionCallInitiator]);
inports = this.codeInfo.Inports;
outports = this.codeInfo.Outports;
component = this.SILPILWrapperUtils.getComponentForDiagnostics();
if numel(inports)~= numel(mF0DataInports)
exception = MSLException(message(errorId, ...
'inports', ...
numel(mF0DataInports), ...
component, ...
numel(inports)));
exception.throw();
end

if numel(outports)~= numel(mF0.outports.toArray)
exception = MSLException(message(errorId, ...
'outports', ...
numel(mF0.outports.toArray), ...
component, ...
numel(outports)));
exception.throw();
end

end

function disallowSampleTimeInheritance = checkSTInheritanceRule(~, disallowSampleTimeInheritance, ~)
% For RLS the error is not thrown because the harness
% locks down the sample time and prevents the subsystem to have
% different sample time between normal (unconstrained) and SIL
% (as specified in code descriptor at 0.2)
end

function [origModel, currModel] = getGlobalDSMRegistrationModels(this, ~)
% A global DSM should be registered to the hidden model
currModel = this.SILPILWrapperUtils.getSILPILInterface().getRootModel();
origModel = currModel;
end

function fcnName = getExportFunctionStepFcnName(this, ~, ~)
% must use same formula as model reference SIM target since we
% are using their classes to generate mdlInitializeSizes (where
% the function call functions get registered)

% Cannot use the function prototype name from codedescriptor as
% it does not match the TaskName in fcnCallInputs of the
% hidden model
fcnCallInputs = this.SILPILWrapperUtils.getInfoStruct().modelInterface.FcnCallInputs;
assert(numel(fcnCallInputs) == 1, ....
'There should be only one function call port');
fcnName = coder.internal.modelreference.FunctionCallUtils.getFcnCallWrapperFcnName(...
fcnCallInputs.TaskName);
end
end
end