classdef (Hidden = true) ExecutionStackSet < coder.profile.ExecutionSet
%EXECUTIONSTACKSET collects execution profiling results (instances of
%coder.profile.ExecutionStack) from different runs of the same model

% Copyright 2023 The MathWorks, Inc.

properties (Access = private)
% profiling results
ResultMap;
end

methods
function obj = ExecutionStackSet(component)
arguments
component (1,1) string
end
% superclass
obj@coder.profile.ExecutionSet(component);
% clean values
obj.ResultMap = containers.Map('KeyType', 'char', 'ValueType', 'any');
end

function add(this, testname, ex)
% add an execution profiling result to the set associated to a
% specific testname

if ~isa(ex, 'coder.profile.ExecutionStack')
% check the type of the input to store
error(message('CoderProfile:Test:SetWrongObject', ...
'coder.profile.ExecutionStack'));
end
if this.ResultMap.Count > 0
% check the profiling result is compatible with the ones we
% already have
ks = this.ResultMap.keys;
if ~this.ResultMap(ks{1}).areComparable(ex)
error(message('CoderProfile:Test:SetDifferentModel'));
end
end
% store it
index = convertStringsToChars(testname);
this.ResultMap(index) = ex;
end

function res = get(obj, testname)
% return the execution profiling result associated to a
% specific testname

% return result if present
res = [];
if obj.ResultMap.isKey(testname)
res = obj.ResultMap(testname);
end
end

function remove(obj, testname)
% remove the execution profiling result associated to a
% specific testname

if obj.ResultMap.isKey(testname)
obj.ResultMap.remove(testname);
end
end
end

methods (Access = public, Hidden)
function res = getSummaryInfo(this)
res.ComponentUnderTest = this.ComponentUnderTest;
if this.ResultMap.Count > 0
ks = this.ResultMap.keys;
res.SimulinkEnvironment = ...
this.ResultMap(ks{1}).isSimulinkWorkflow();
else
res.SimulinkEnvironment = [];
end
res.NumTests = this.ResultMap.Count;
r = this.getTaskSummary();
res.NumTasks = numel(unique(r.Task));
res.TimeCreated = this.TimeCreated;
end

function tbt = getTaskSummary(this)
testNameCln = cell(1,0);
taskNameCln = cell(1,0);
maxStackCln = zeros(1,0);
maxStackAtCln = zeros(1,0);
avgStackCln = zeros(1,0);
numCallsCln = zeros(1,0);

keys = this.ResultMap.keys;
numKeys = length(keys);
for i = 1:numKeys
tstName = keys{i};
if isempty(this.ResultMap(tstName))
continue;
end
sections = this.ResultMap(tstName).TaskSections;
for j = 1:length(sections)
section = sections(j);
t = section.getTraceInfo();
sampleTime = t.getSampleTime;
if isinf(sampleTime(1))
continue;
end
[sortedUsage, idxs] = sort(section.StackUsage, 'descend');
numElems = numel(sortedUsage);
if numElems > this.ResultsPerTest
numElems = this.ResultsPerTest;
end
for z = 1:numElems
testNameCln{end+1} = tstName; %#ok<AGROW>
taskNameCln{end+1} = section.Name; %#ok<AGROW>
maxStackCln(end+1) = sortedUsage(z); %#ok<AGROW>
avgStackCln(end+1) = section.AverageStackUsage; %#ok<AGROW>
maxStackAtCln(end+1) = section.StackTime(idxs(z)); %#ok<AGROW>
numCallsCln(end+1) = section.NumCalls; %#ok<AGROW>
end
end
end

tbt = table(taskNameCln', maxStackCln', maxStackAtCln', ...
avgStackCln', numCallsCln', ...
testNameCln');
tbt.Properties.VariableNames = {'Task', ...
'MaxStack', 'MaxStackAt', 'AvgStack', 'NumCalls', 'TestName'};
tbt = sortrows(tbt, {'MaxStack'}, {'descend'});
end
end
end