classdef CallerFunctionInfo < rtw.pil.SimulinkCSFunctionInfo & rtw.pil.BaseCallerFunctionInfo
%CALLERFUNCTIONINFO: representation for caller client function (in
% Client/Server semantics)

% Copyright 2014-2019 The MathWorks, Inc.

methods (Access = {?rtw.pil.CSFunctionInfoFactory})
% Only allow construction by the factory class
function this = CallerFunctionInfo(codeInfoData, ...
operationName, ...
rtmTypeIdentifier, ...
slFunctionInfo, ...
tid,...
fcnId,...
isDefined,...
isAUTOSAR, ...
isLegacyAutosarOperation)

% validate inputs
assert(rtw.connectivity.CodeInfoUtils.isa(codeInfoData, 'AutosarClientCall'), ...
'codeInfoData should be AutosarClientCall');
assert(isnumeric(fcnId) && isscalar(fcnId) && ~isempty(fcnId),...
'fcnId has to be numeric and scalar value');

% call superclass constructors
this@rtw.pil.SimulinkCSFunctionInfo(codeInfoData, operationName, rtmTypeIdentifier, slFunctionInfo, tid, isAUTOSAR, isLegacyAutosarOperation);
this@rtw.pil.BaseCallerFunctionInfo(fcnId, isDefined);

% sets the function name of the caller on the target. For AUTOSAR,
% the function name is normally Rte_Call_<PortName>_<OperationName>
if this.getIsAUTOSAR
portName = this.getCodeInfoData.PortName;
operationName = this.getOperationName;
this.TargetFunctionName = sprintf('Rte_Call_%s_%s', portName, operationName);
else
% ERT - just the function prototype name
this.TargetFunctionName = this.getCodeInfoData.Prototype.Name;
end
end
end

methods
%Write out S-Function API calls need to call the Caller function
%correctly.
function writeCallToFunction(this, writer, simStructArg, argsVarName, argPointerAccessString)
slFcnName = this.getSlFunctionName;
if ~this.isLegacyAUTOSARClientBlock
writer.wBlockStart('if (slcsIsFunctionRegistered(%s "%s"))',...
simStructArg, slFcnName);
writer.wComment(['call the Simulink function: ' slFcnName]);
writer.wLine('slcsInvokeSimulinkFunction(%s"%s", &%s%s);', ...
simStructArg, slFcnName, argsVarName, ...
argPointerAccessString);
writer.wBlockEnd; %slcsIsFunctionRegistered

end
end

%Define any arguments used by the Caller function
function argPointerAccessString = defineArgsForSLCall(this, writer, argVarName)
numInOutArgs = this.getNumInOutArgs;
numInputArgs = this.getNumInArgs + numInOutArgs;
numOutputArgs = this.getNumOutArgs + this.getNumReturnArg + numInOutArgs;

argCount = numInputArgs+numOutputArgs;
if argCount == 0
argSizeString = '';
argPointerAccessString = '';
else
argSizeString = sprintf('[%d]', argCount);
argPointerAccessString = '[0]';
end

writer.writeLine('_ssFcnCallExecArgInfo %s%s;', argVarName,...
argSizeString);
end

%Write out the assignment of arguments for the caller
function writeSfunctionDataAssignmentForSLCall(this, sfunObj, writer, argVarName)
writer.wBlockStart('if (ssGetPWork(S) != NULL)');
numInOutArgs = this.getNumInOutArgs;
numInputArgs = this.getNumInArgs + numInOutArgs;

args = this.getAllCodeInfoArgs;
for codeInfoArgIdx = 1:length(args)
writer.wBlockStart(''); % scope around each argument
arg = args(codeInfoArgIdx);
codeArgPosition = this.getCodeArgPosition(arg);
argDataInterface = this.getDataInterfacesForArgs(codeArgPosition);
dataElements = this.getDataElementsForArgs(codeArgPosition);

% the first data element has all the info we need
cData = dataElements(1);
if cData.isStructElement
[~, ~, hostElementSize] = cData.getStructArrayTopLevelInfo;
if hostElementSize.needsDTA
% access the API that will give us Bus size information
writer.writeLine('slDataTypeAccess * dta = ssGetDataTypeAccess(S);');
writer.writeLine('const char * bpath = ssGetPath(S);');
end
end

% set dataPtr and dataSize
isInArg = strcmp(arg.IOType, 'INPUT');
isInoutArg = strcmp(arg.IOType, 'INPUT_OUTPUT');
slArgPosition = this.getSlArgPosition(codeArgPosition);
if isInoutArg
% in arg
this.writeArgDataPtrAndSizeAssignment(sfunObj,...
argDataInterface, argVarName, slArgPosition(1));
slArgPosition = slArgPosition + numInputArgs;
% out arg
this.writeArgDataPtrAndSizeAssignment(sfunObj,...
argDataInterface, argVarName, slArgPosition(2));
else
if ~isInArg
slArgPosition = slArgPosition + numInputArgs;
end
this.writeArgDataPtrAndSizeAssignment(sfunObj,...
argDataInterface, argVarName, slArgPosition);
end
writer.wBlockEnd; % scope around each argument
end
writer.wBlockEnd; %if (ssGetPWork(S) != NULL)
end

function [sid, argTypePrefix, argNamePrefix, argConstructor] = getArgDataInfo(this)
argTypePrefix = 'caller';
argNamePrefix = 'xsd_call';
sid = this.getCodeInfoData.SID;
argConstructor = @rtw.pil.CallerFunctionArgData;
end
end

methods (Access = 'private')
function writeArgDataPtrAndSizeAssignment(this, sfunObj, ...
argDataInterface, argVarName, slArgPosition)
pWorkIdx = sfunObj.getCallerFunctionArgumentPWorkIdx(this, argDataInterface);
slArgPositionStr = num2str(slArgPosition-1); %0-based

argPtr = sprintf('%s[%s].dataPtr', argVarName, slArgPositionStr);
sfunObj.getPWorkValue('void', argPtr , pWorkIdx, false);
end
end
end