classdef (Hidden = true) MATLABTargetInterface < rtw.pil.TargetInterface
%MATLABTARGETINTERFACE creates a standalone MATLAB target interface file
%
% MATLABTARGETINTERFACE creates a standalone MATLAB 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 2016-2020 The MathWorks, Inc.

% properties
properties (SetAccess = 'private', GetAccess = 'private')
UseCoderDataMarshalling = false; % Switch to defer to MATLAB Coder Serialization
interfaceDataList = {}; %Need to maintain the order of the arguments,
%so use a simple cell array. A map would be
%sorted. But the function arguments must
%maintain their canonical order.
InstrFcnInEntryPoints = []; % instrumentation functions to be
% called in xilPreEntryPoint (first
% item) and in xilPostEntryPoint
% (second item)
end

methods
% constructor
function this = MATLABTargetInterface(silPILWrapperUtils, ...
targetInterfacePath)
narginchk(2, 2);
% call super class constructor
this@rtw.pil.TargetInterface(silPILWrapperUtils, ...
targetInterfacePath);
end
end

methods (Access = 'protected')
function writeSectionStorage(~)
%Override Super Class function
end

function writeSectionIncludes(this)

writeSectionIncludes@rtw.pil.TargetInterface(this);

% Include serialize/de-serialize function definitions
fcnName = this.SILPILWrapperUtils.getComponentName;
this.writer.wLine('#include "_coder_%s_target.h"', fcnName)
targetInterfaceHeader = 'xil_target_interface.h';
this.writeXILTargetInterfaceHeader(targetInterfaceHeader);
this.writer.wLine('#include "%s"',targetInterfaceHeader);
this.writer.newLine;
end

function writeSectionXILIOData(~)
%Override Super Class function
end

function writeSectionExternalInitialization(~)
%Override Super Class function
end

function writeSectionInitBody(this)
initFun = this.codeInfo.InitializeFunctions;

if ~isempty(initFun)
isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
if isExecutionProfilingEnabled
% if profiling is active, the overhead can be filtered
this.writeSectionOverheadConfiguration();
end

this.writer.wComment('Invoke Initialization Function');
this.writer.wBlockStart('if (xilFcnId == 0)');
% call initialize function
if isExecutionProfilingEnabled

[sectionIdForFunction, instrFcn] =...
this.getProfilingIdentifier(initFun);

if isempty(this.InstrFcnInEntryPoints)
this.InstrFcnInEntryPoints.startFcn = instrFcn{1};
this.InstrFcnInEntryPoints.endFcn = instrFcn{2};
else
assert(strcmp(this.InstrFcnInEntryPoints.startFcn, instrFcn{1}) && ...
strcmp(this.InstrFcnInEntryPoints.endFcn, instrFcn{2}), ...
'Only TASK_TIME_PROBEs are supported for task-level instrumentation');
end
else
sectionIdForFunction = 0;
end

% call deferred initialize function
this.writer.wLine('XILTarget_initialize(%dU);',sectionIdForFunction);

this.writer.wBlockMiddle('else');
this.writer.writeLine('errorCode = XIL_INTERFACE_UNKNOWN_FCNID;');
this.writer.wBlockEnd('}');
end
end

function writeSectionInitDataProcessingCase(~, ~, ~, ~, ~)
%Override Super Class function
end

% write the xilIOData table variable
function writePILIODataTable(~, ~, ~)
%Override Super Class function
end

function writeSectionOutputBody(this)
this.writer.writeCellLines({'/* Single In-the-Loop Component */';
'if (xilFcnId != 0) {';
' return XIL_INTERFACE_UNKNOWN_FCNID;';
'}';});

outputTasks = this.getOutputTasks;
isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;

this.writer.wBlockStart('switch(xilTID)');

for taskIdx = 1:length(outputTasks)
outputTask = outputTasks(taskIdx);

this.writer.writeLine(['case ' int2str(outputTask.id) ':']);
this.writer.incIndent;
if isExecutionProfilingEnabled

[sectionIdForFunction, instrFcn] =...
this.getProfilingIdentifier(...
outputTask.codeInfoData);

if isempty(this.InstrFcnInEntryPoints)
this.InstrFcnInEntryPoints.startFcn = instrFcn{1};
this.InstrFcnInEntryPoints.endFcn = instrFcn{2};
else
assert(strcmp(this.InstrFcnInEntryPoints.startFcn, instrFcn{1}) && ...
strcmp(this.InstrFcnInEntryPoints.endFcn, instrFcn{2}), ...
'Only TASK_TIME_PROBEs are supported for task-level instrumentation');
end
else
sectionIdForFunction = 0;
end

% call output function
this.writer.wBlockStart('if ( xilTarget_%s(%dU) != XIL_PROCESSDATA_SUCCESS )',outputTask.codeInfoData.Prototype.Name,sectionIdForFunction);
this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
this.writer.wBlockEnd(); %if xilTarget_<fcn>() != XIL_PROCESSDATA_SUCCESS

this.writer.writeLine('break;');
this.writer.decIndent;
end

% generate default
this.writer.writeCellLines({'default:';
' return XIL_INTERFACE_UNKNOWN_TID;'});
this.writer.wBlockEnd(''); %switch(xilTID)

if this.SILPILWrapperUtils.AreEventCallbacksRequired
this.writer.writeLine('StepCompletedEvent();');
this.writer.newLine;
end

this.writer.writeLine('return XIL_INTERFACE_SUCCESS;');
end

function writeOutputBody(this)

writeOutputBody@rtw.pil.TargetInterface(this);

this.writeSectionPrePostEntryPoints();

end

function writeSectionTerminateBody(this)

termFun = this.codeInfo.TerminateFunctions;
if ~isempty(termFun)
isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;
if isExecutionProfilingEnabled

[sectionIdForFunction, instrFcn] =...
this.getProfilingIdentifier(termFun);

if isempty(this.InstrFcnInEntryPoints)
this.InstrFcnInEntryPoints.startFcn = instrFcn{1};
this.InstrFcnInEntryPoints.endFcn = instrFcn{2};
else
assert(strcmp(this.InstrFcnInEntryPoints.startFcn, instrFcn{1}) && ...
strcmp(this.InstrFcnInEntryPoints.endFcn, instrFcn{2}), ...
'Only TASK_TIME_PROBEs are supported for task-level instrumentation');
end
else
sectionIdForFunction = 0;
end

% call deferred terminate function
this.writer.wLine('XILTarget_terminate(%dU);',sectionIdForFunction);

if this.SILPILWrapperUtils.AreEventCallbacksRequired
this.writer.newLine;
this.writer.writeLine('TerminateEvent();');
end
end
end

function indices = getParamIndicesForStorage(~)
% default implementation, overwritten by subclasses
indices = [];
end
end
methods (Access = 'private')

function writeSectionPrePostEntryPoints(this)
isExecutionProfilingEnabled = this.SILPILWrapperUtils.isProfilingEnabled;

this.writer.wBlockStart('void xilPreEntryPoint(uint32_T entryPointID)');
if isExecutionProfilingEnabled
assert(~isempty(this.InstrFcnInEntryPoints), ...
'There must be at least an entry point');
this.writer.writeLine('%s(entryPointID);', ...
this.InstrFcnInEntryPoints.startFcn);
else
this.writer.wLine('UNUSED_PARAMETER(entryPointID);');
end

this.writer.wBlockEnd(); %Pre Entry Point function

this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilPostEntryPoint(uint32_T entryPointID)');
this.writer.wLine('MemUnit_T responseId = XIL_RESPONSE_OUTPUT_DATA;');

if isExecutionProfilingEnabled
this.writer.writeLine('%s(entryPointID);', ...
this.InstrFcnInEntryPoints.endFcn);
else
this.writer.wLine('UNUSED_PARAMETER(entryPointID);');
end

% Need to send the response id before sending back data
this.writer.wBlockStart('if(xilWriteData(&responseId, sizeof(responseId)) != XIL_DATA_STREAM_SUCCESS)');
this.writer.wLine('return XIL_INTERFACE_COMMS_FAILURE;');
this.writer.wBlockEnd(); %If send response Id.

this.writer.wLine('return XIL_INTERFACE_SUCCESS;');
this.writer.wBlockEnd(); %Post Entry Point function

termFun = this.codeInfo.TerminateFunctions;
if ~isempty(termFun)
%If there is a terminate function we need to a
%different Post Hook function
this.writer.wBlockStart('XIL_INTERFACE_ERROR_CODE xilPostTerminatePoint(uint32_T entryPointID)');

if isExecutionProfilingEnabled
this.writer.writeLine('%s(entryPointID);', ...
this.InstrFcnInEntryPoints.endFcn);
else
this.writer.wLine('UNUSED_PARAMETER(entryPointID);');
end

this.writer.wLine('return XIL_INTERFACE_SUCCESS;');
this.writer.wBlockEnd(); %Post Terminate Point function
end
end

function writeXILTargetInterfaceHeader(this, targetInterfaceHeader)
% Write Host Interface header file
headerPath = fileparts(this.writerOutputPath);
hWriter = coder.internal.connectivity.TargetInterfaceHeaderWriter(headerPath, ...
targetInterfaceHeader);
append = false;
callCBeautifier = true;
obfuscateCode = this.SILPILWrapperUtils.shouldObfuscateXILWrapperFiles;
hWriter.writeOutput(append, callCBeautifier, obfuscateCode);
end
end
end