classdef (Hidden = true) ExecTimeData < handle
%EXECTIMEDATA provides access to execution profiling results 
%

%   Copyright 2011-2023 The MathWorks, Inc.

    properties (Access=private) 
        ProfiledSections;
        TimerStartValues;
        TimerEndValues;
        SelfTimerValues;
        ExecutionTimeValues;
        SimulationTimeValues;
        SectionIds;
        NodeIds;
        CoreNumStart;
        CoreNumEnd;
        AnalysisInconsistencies;
        Nodes;
        TotalTurnaroundTimes;
        TotalExecutionTimes;
        MaxTurnaroundTimes;
        MaxTurnaroundTimesCallNum;
        MaxExecutionTimes;
        MaxExecutionTimesCallNum;
        TotalSelfTimes;
        MaxSelfTimes;
        MaxSelfTimesCallNum;
        NumCalls;
        ErrorCodes;
        VisibleSections;
        IdUint64;
        TaskSuspendSectionIds;
        IsTaskSuspendSection;
        IsMultiCore;
        RealTimeNumInconsistencies;
        RealTimeFirstInconsistency;
        TimerSlotStartValues;
        NumSamples;
        
        TaskOverhead = [];
        FunctionOverhead = [];
        TimestampOverhead = [];
        FreezingOverhead = [];

        OverrunTable = [];
    end
    
    properties (SetAccess=public, GetAccess=private)
        CodeProfilingSaveOptions;
        ModelForProfilingTopSettings;
        OriginalComponentName;
        TimerTicksPerSecond;
    end
    
    properties (SetAccess=private, GetAccess=public)
        UniqueIds;
    end

    
    
    methods (Access=private)
        
        
        function updateTimeSlotInfo(this)
        % Each invocation of a periodic task has an associated time slot. The slot
        % starts when a task becomes due to run and ends at deadline by which
        % time the task must be complete. We can identify (relative to the
        % base-rate) when a task becomes due to run by using knowledge of sample
        % periods and offsets.

            lSections = this.ProfiledSections;
            isPeriodicTask = false(size(lSections));
            lSampleTimes = zeros(length(isPeriodicTask), 2);
            baseRateSamplePeriod = Inf;
            for i=this.VisibleSections
                isTask = lSections{i}.getTraceInfo.isTask;
                if isTask
                    lSamplePeriod = lSections{i}.SamplePeriod;
                    if isfinite(lSamplePeriod)
                        isPeriodicTask(i) = true;
                        if lSamplePeriod < baseRateSamplePeriod
                            baseRateSamplePeriod = lSamplePeriod;
                        end
                        lSampleTimes(i, :) = [lSamplePeriod lSections{i}.SampleOffset];
                    end
                end
            end
            
            % Each task start timer value is associated with a base rate counter
            % corresponding to the time slot where the invocation becomes due to
            % run
            lSlotCounter = uint64(zeros(size(this.TimerStartValues)));
            
            % Track the timer start values corresponding to the start of a periodic task
            taskStartValueIdx = false(size(this.TimerStartValues));
            
            % Identify the timer value corresponding to the start of each base rate time
            % slot. Note that there may be more than one base rate, and (on a
            % multi-core system) it is possible that the measured start time of a
            % sub-rate is prior to the start time of the corresponding base
            % rate. 
            tSlotStart = [];
            lTaskSections = find(isPeriodicTask);
            lTaskSampleTimes = lSampleTimes(isPeriodicTask, :);
            
            % We need to process in order of priority, starting with the base rate
            [~, priorityOrder] = sort(lTaskSampleTimes(:,1));
            
            for i=1:length(priorityOrder)
                idx = priorityOrder(i);
                sectionNumber=lTaskSections(idx);
                
                sectionIdx = (sectionNumber+1)==this.NodeIds;
                tTaskStart = this.TimerStartValues(sectionIdx);
                
                Tmult = lTaskSampleTimes(idx, :)/baseRateSamplePeriod;
                [tSlotStart, taskSlotCounter] = ...
                    coder.profile.slotSyncTask(tTaskStart, Tmult, tSlotStart);
                
                taskStartValueIdx = taskStartValueIdx | sectionIdx;
                
                lSlotCounter(sectionIdx) = taskSlotCounter;
            end
            
            this.TimerSlotStartValues = uint64(zeros(size(this.TimerStartValues)));
            this.TimerSlotStartValues(taskStartValueIdx) = ...
                tSlotStart(lSlotCounter(find(taskStartValueIdx))); %#ok
                    
        end
        
        
        function generateIdUint64(this)
        % Generate a 64-bit random number to serve as a unique identifier
            s = rng;
            rng('shuffle', 'twister');
            imax32 = intmax('uint32');
            xrand = uint64(randi(imax32, 2, 'uint32'));
            this.IdUint64 = xrand(1)+xrand(2)*(uint64(imax32)+1);
            rng(s);
        end
            
        
        function idx = getNodeIdxFromSectionNumber(this, sectionNumber)
            
        % The root node does not count as a profiled section
            nodeNumber = sectionNumber + 1;
            idx = this.NodeIds==nodeNumber;           
        end
    
        
        function initializeDataOnTarget(this, lGlobalRegistry)
            if isempty(lGlobalRegistry)
                numTimeSections = 0;
            else
                numTimeSections = lGlobalRegistry.TotalTimeProbe;
                numSections = length(lGlobalRegistry.getAllProbes);
                lSectionIds = 1:numSections;
                lProfiledSections = cell(numTimeSections, 1);
                
                lSectionTraceInfos = lGlobalRegistry.getInfoForSectionId(lSectionIds);
                
                idx = 1;
                for i=1:lGlobalRegistry.TotalTimeProbe+3
                    lSectionTraceInfo = lSectionTraceInfos{i};
                    if ~isempty(lSectionTraceInfo)
                        if lSectionTraceInfo.isTask
                            lProfiledSections{idx} = coder.profile.ExecutionTimeTaskSection...
                                (this, idx, lSectionTraceInfo);
                        else
                            lProfiledSections{idx} = coder.profile.ExecutionTimeCodeSection...
                                (this, idx, lSectionTraceInfo);
                        end
                        idx = idx + 1;
                    end
                end
                this.ProfiledSections = lProfiledSections;
            end
            this.IsTaskSuspendSection = false(numTimeSections, 1);
            this.VisibleSections = 1:numTimeSections;
       end

       function initializeDataOnHost(this, lGlobalRegistry)
            
            this.consistencyCheck;

            % Skip the root node
            numSections = length(this.Nodes(1,:)) - 1;

            lSectionIds = this.getSectionIdsForNode(1:numSections);
            
            this.IsTaskSuspendSection = false(numSections, 1);
            for i=1:length(this.TaskSuspendSectionIds)
                suspendId = this.TaskSuspendSectionIds(i);
                this.IsTaskSuspendSection(lSectionIds==suspendId)=true;
            end
            
            hiddenSections = lSectionIds==0;

            % Need a mapping in order to filter out the hidden sections (i.e. preemption
            % sections) from reports and analysis
            tmpAllSections = 1:numSections;
            this.VisibleSections = tmpAllSections(~hiddenSections);
            
            if ~isempty(lGlobalRegistry)
                
                lProfiledSections = cell(numSections, 1);
                
                hiddenSections = false(numSections, 1);
                
                isPreemptionSection = lSectionIds==0;
                lSectionTraceInfos = cell(size(lSectionIds));
                lSectionTraceInfos(~isPreemptionSection) = ...
                    lGlobalRegistry.getInfoForSectionId(lSectionIds(~isPreemptionSection));
                
                for i=1:numSections
                    
                    if isPreemptionSection(i)
                        lProfiledSections{i} = coder.profile.ExecutionTimePreemptionSection...
                            (this, i);
                        hiddenSections(i)=true;
                    else
                        lSectionTraceInfo = lSectionTraceInfos{i};
                        
                        if lSectionTraceInfo.isTask 
                            lProfiledSections{i} = coder.profile.ExecutionTimeTaskSection...
                                (this, i, lSectionTraceInfo);
                        else
                            lProfiledSections{i} = coder.profile.ExecutionTimeCodeSection...
                                (this, i, lSectionTraceInfo);
                        end
                    end
                end
                
                this.ProfiledSections = lProfiledSections;
            end
       end
    end

    
    methods (Access=public)
        
        
        function errorIfSummaryDataOnly(this)
            
            if ~strcmp(this.getCodeProfilingSaveOptions, 'AllData')
                allDataStr = DAStudio.message...
                    ('RTW:configSet:ERTDialogProfilingSaveAllOpt');
                mdl = this.getModelForProfilingTopSettings;
                if ~isempty(mdl)
                    extendedMsg = DAStudio.message...
                        ('CoderProfile:ExecutionTime:ExecProfSummaryDataOnlyExtended', ...
                         this.getModelForProfilingTopSettings,...
                         allDataStr);
                else
                    extendedMsg = ' ';
                end
                DAStudio.error('CoderProfile:ExecutionTime:ExecProfSummaryDataOnly', ...
                               extendedMsg);
            end
        end
        
        function lIsMultiCore = getIsMultiCore(this)
            if isempty(this.IsMultiCore)
                this.IsMultiCore = ~isempty(this.CoreNumStart) ...
                    && any(this.CoreNumStart~=this.CoreNumStart(1));
            end
            lIsMultiCore = this.IsMultiCore;
        end
        
        function lIsTaskSuspendSection = getIsTaskSuspendSection(this)
            lIsTaskSuspendSection = this.IsTaskSuspendSection;
        end
        
        function id = getIdUint64(this)
            id = this.IdUint64;
        end
        
        function val = isSummaryDataOnly(this)
        % Test if only summary data is saved
            validOption = false;
            switch this.CodeProfilingSaveOptions
              case {'SummaryOnly', 'MetricsOnly'}
                val = true;
                validOption = true;
              case 'AllData'
                val = false;
                validOption = true;
            end
            assert(validOption, 'CodeProfilingSaveOptions must be valid');
        end
        
        function val = isOnTargetData(this)
            val = strcmp(this.CodeProfilingSaveOptions, 'MetricsOnly');
        end
        
        function saveOpts = getCodeProfilingSaveOptions(this)
            saveOpts = this.CodeProfilingSaveOptions;
        end
        
        function [timerMin, timerMax, numPoints] = getTimerMinMax(this)
            timerMin = min(this.TimerStartValues);
            timerMax = max(this.TimerEndValues);
            numPoints = length(this.TimerStartValues);
        end

        function res = getNumSamples(this)
            res = this.NumSamples;
        end
        
        function [timerStartValues, timerEndValues, ...
                lCoreNumStart, lCoreNumEnd,...
                nodeNumber, analysisInconsistency, lSimTimes] ...
                = getTimerValues(this)
                            
            timerStartValues = this.TimerStartValues;
            timerEndValues = this.TimerEndValues;
            lCoreNumStart = this.CoreNumStart;
            lCoreNumEnd = this.CoreNumEnd;
            nodeNumber = this.NodeIds;
            if ~isempty(this.AnalysisInconsistencies) 
                % only reported for real-time case
                analysisInconsistency = this.AnalysisInconsistencies;
            else
                analysisInconsistency = [];
            end
            lSimTimes = this.SimulationTimeValues;
        end
        
        function lNodeIds = getNodeIds(this)
            lNodeIds = this.NodeIds;
        end
        
        function val = getTimerTicksPerSecond(this)
            val = this.TimerTicksPerSecond;
        end
        
        function lTotalTime = getTotalExecutionTime(this)
            
            rootNodeNum = 1;
            
            % Self time for the root node does not count towards
            % total execution time
            lTotalTime = this.TotalExecutionTimes(rootNodeNum) ...
                - this.TotalSelfTimes(rootNodeNum);
        
        end

        function lTotalTime = getTotalTurnaroundTime(this)
            
            rootNodeNum = 1;
            
            lTotalTime = this.TotalTurnaroundTimes(rootNodeNum);
        
        end

        function values = getTotalTurnaroundTimes(this)
            values = this.TotalTurnaroundTimes;
        end

        function values = getTotalExecutionTimes(this)
            values = this.TotalExecutionTimes;
        end

        function values = getMaxExecutionTimes(this)
            values = this.MaxExecutionTimes;
        end

        function values = getMaxExecutionTimesCallNum(this)
            values = this.MaxExecutionTimesCallNum;
        end

        function values = getMaxTurnaroundTimes(this)
            values = this.MaxTurnaroundTimes;
        end

        function values = getMaxTurnaroundTimesCallNum(this)
            values = this.MaxTurnaroundTimesCallNum;
        end

        function values = getTotalSelfTimes(this)
            values = this.TotalSelfTimes;
        end

        function values = getMaxSelfTimes(this)
            values = this.MaxSelfTimes;
        end

        function values = getMaxSelfTimesCallNum(this)
            values = this.MaxSelfTimesCallNum;
        end

        function values = getNumCalls(this)
            values = this.NumCalls;
        end

        function values = getErrorCodes(this)
            values = this.ErrorCodes;
        end        
        
        function lNodes = getNodes(this)
            
            lNodes = this.Nodes;

        end

        function values = getTimerStartValues(this)
            
            values = this.TimerStartValues;

        end
        
        function values = getSelfTimerValues(this)
            
            values = this.SelfTimerValues;

        end
        
        function values = getExecutionTimeValues(this)
            
            values = this.ExecutionTimeValues;

        end
        
        function numSectionNodes = getNumSectionNodes(this)
            
            % We only count the visible nodes (i.e. not preemption nodes)
                numSectionNodes = length(this.VisibleSections);
                
        end
        
        function [ticksMax, callNum] = getMaxTurnaroundTicksForSection...
            (this, sectionNumber)

            nodeNumber = sectionNumber + 1;
            
            ticksMax = this.MaxTurnaroundTimes(nodeNumber);
            callNum = this.MaxTurnaroundTimesCallNum(nodeNumber);

        end
                
        function [ticksMax, callNum] = getMaxExecutionTicksForSection...
            (this, sectionNumber)

            nodeNumber = sectionNumber + 1;
            
            ticksMax = this.MaxExecutionTimes(nodeNumber);
            callNum = this.MaxExecutionTimesCallNum(nodeNumber);

        end

        function [selfTicksMax, callNum] = getMaxSelfTicksForSection...
            (this, sectionNumber)

            nodeNumber = sectionNumber + 1;
            
            selfTicksMax = this.MaxSelfTimes(nodeNumber);
            callNum = this.MaxSelfTimesCallNum(nodeNumber);

        end
                
        function ticksTotal = getTotalExecutionTicksForSection...
                (this, sectionNumber)
            
            nodeNumber = sectionNumber + 1;
            
            ticksTotal = this.TotalExecutionTimes(nodeNumber);
            
        end

                
        function ticksTotal = getTotalTurnaroundTicksForSection...
                (this, sectionNumber)
            
            nodeNumber = sectionNumber + 1;
            
            ticksTotal = this.TotalTurnaroundTimes(nodeNumber);
            
        end

                
        function selfTicksTotal = getTotalSelfTicksForSection...
                (this, sectionNumber)

            nodeNumber = sectionNumber + 1;

            selfTicksTotal = this.TotalSelfTimes(nodeNumber);

        end
        
        function numCalls = getNumCallsForSection(this, sectionNumber)

            nodeNumber = sectionNumber + 1;

            numCalls = this.NumCalls(nodeNumber);
        end
        
        function val = getSimulationStartTime(this)
            val = 0; % Default
            if ~isempty(this.SimulationTimeValues)
                val = this.SimulationTimeValues(1);
            end
        end
        
        function lSimulationTimeValues = ...
                getSimulationTimeForNode(this, sectionNumber)
            
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            
            lSimulationTimeValues = this.SimulationTimeValues(idx);
        end
         
        function lTimerStartValues = ...
                getTimerStartValuesForNode(this, sectionNumber)
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lTimerStartValues = this.TimerStartValues(idx);
        end
        
        function lTimerEndValues = ...
                 getTimerEndValuesForNode(this, sectionNumber, callNum)
             
            if nargin < 3
                callNum=0;
            end

            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lTimerEndValues = this.TimerEndValues(idx);
            
            if callNum > 0
                lTimerEndValues = lTimerEndValues(callNum);
            end
            
         end
        
        function lTimerSlotStartValues = ...
                 getTimerSlotStartValuesForNode(this, sectionNumber, callNum)
             
            if nargin < 3
                callNum=0;
            end
            
            lTimerSlotStartValues = [];
            
            if isinf(this.ProfiledSections{sectionNumber}.SamplePeriod)
                return;
            end
            
            if isempty(this.TimerSlotStartValues)
               updateTimeSlotInfo(this);
            end
            
            nodeIdx = getNodeIdxFromSectionNumber(this, sectionNumber);
            
            lTimerSlotStartValues = this.TimerSlotStartValues(nodeIdx);


            if callNum > 0
                lTimerSlotStartValues = lTimerSlotStartValues(callNum);
            end
            
         end
         
        function lSelfTimeValues = ...
                getSelfTimeValuesForNode(this, sectionNumber)
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lSelfTimeValues = this.SelfTimerValues(idx);
         end
         
        function lExecutionTimeValues = ...
                getExecutionTimeValuesForNode(this, sectionNumber)
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lExecutionTimeValues = this.ExecutionTimeValues(idx);
        end
         
        function lCoreNumStart = ...
                getCoreNumStartForNode(this, sectionNumber)
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lCoreNumStart = this.CoreNumStart(idx);
         end
         
         function [numInconsistencies, lTimeOfFirstInconsistency] = ...
                 getAnalysisInconsistencies(this, sectionNumber)
             lTimeOfFirstInconsistency = [];
             numInconsistencies = 0;
             
             % The root node does not count as a profiled section
             nodeNumber = sectionNumber + 1;
             
             % Always non-empty except if loading data save with older classdef
             if ~isempty(this.RealTimeNumInconsistencies)
                 numInconsistencies = this.RealTimeNumInconsistencies(nodeNumber);
                 if this.RealTimeNumInconsistencies(nodeNumber) > 0
                     lTimeOfFirstInconsistency = this.RealTimeFirstInconsistency(nodeNumber);
                     if ~isempty(this.TimerTicksPerSecond)
                         lTimeOfFirstInconsistency = ...
                             double(lTimeOfFirstInconsistency)/this.TimerTicksPerSecond;
                     end
                 end
             end
         end
         
         function lCoreNumEnd = ...
                 getCoreNumEndForNode(this, sectionNumber)
            idx = this.getNodeIdxFromSectionNumber(sectionNumber);
            lCoreNumEnd = this.CoreNumEnd(idx);
         end
         
         function numNodes = getNumNodes(this)
             numNodes = length(this.Nodes(1,:));
         end
             
                  
        function parentNumber = getParentNumber(this, sectionNumber)
            
            % The root node does not count as a profiled section
            nodeNumber = sectionNumber + 1;
            
            node = this.Nodes(:,nodeNumber);
            
            parentNumber = node(1)-1;
            
        end
        
        function childNumbers = getChildNumbers(this, sectionNumber)
            
            % The root node does not count as a profiled section
            nodeNumber = sectionNumber + 1;
            
            childNumbers = find(this.Nodes(1,:)==nodeNumber);
            
            childNumbers = childNumbers -1;
            
        end
        
        function lSectionIds = getSectionIdsForNode...
                (this, sectionNumbers)
            
            % The root node does not count as a profiled section
            nodeNumbers = sectionNumbers+1;
            lSectionIds = this.SectionIds(nodeNumbers);

        end
        
        
        function consistencyCheck(this)

            if any(this.ErrorCodes)
                % We do not hit this error in the real-time case because non-matching start/end
                % data points are handled earlier
                DAStudio.error...
                    ('CoderProfile:ExecutionTime:ProfilingDataInconsistent');
            end
            
            if any(this.RealTimeNumInconsistencies)
                MSLDiagnostic('CoderProfile:ExecutionTime:DataPointInconsistency',...
                                 sum(this.RealTimeNumInconsistencies)).reportAsWarning;
            end
            
        end
        
        function slotUtilizationPercent = getSlotUtilization(this, sectionNumber, period)
        % Return slot utilization in percent for each invocation of this task section
            
            slotUtilizationPercent = [];
            
            timerSlotStartValues = getTimerSlotStartValuesForNode(this, sectionNumber);
            
            if isempty(timerSlotStartValues)
                return;
            end
            
            timerEndValues = getTimerEndValuesForNode(this, sectionNumber);
            
            slotUtilizationPercent = 100 ...
                * double((timerEndValues-timerSlotStartValues)) ...
                / this.TimerTicksPerSecond...
                / period;
            
        end
        
        function initialize(this, lGlobalRegistry)
            this.consistencyCheck;

            if this.isOnTargetData
                this.initializeDataOnTarget(lGlobalRegistry);
            else
                this.initializeDataOnHost(lGlobalRegistry);
            end
        end
    end
    
    methods (Access=public, Hidden = true)
        function [task, fcn, timestamp, freezing] = getOverhead (this)
            task = this.TaskOverhead;
            fcn = this.FunctionOverhead;
            timestamp = this.TimestampOverhead;
            freezing = this.FreezingOverhead;
        end

        function res = hasOverrunCount(this)
            res = ~isempty(this.OverrunTable) && ...
                ~isempty(this.OverrunTable.ID);
        end

        function res = getOverrunCount(this, taskName)
            if isempty(this.OverrunTable)
                res = -1;
                return;
            end
            idx = find(strcmp(taskName, this.OverrunTable.Name));
            if isempty(idx)
                res = uint64(0);
                return;
            end
            res = this.OverrunTable.NumEvents(idx);
        end
    end
    
    methods (Access=public)
        
        function visibleNumber = getVisibleSectionNumber(this, number)
            
            visibleNumber = find(this.VisibleSections==number);
        
        end

        function sectionProfile = getVisibleSectionProfile(this, sectionNumber)
            
            % We filter out the hidden (i.e. preemption) sections
            actualSectionNumber = this.VisibleSections(sectionNumber);
            sectionProfile=this.ProfiledSections{actualSectionNumber};
        end
        
        function sectionProfile = getSectionProfile(this, sectionNumber)

            if sectionNumber==0
                % special case for root section
                sectionProfile = coder.profile.ExecutionTimeRootSection...
                    (this, 0);
            else
                sectionProfile=this.ProfiledSections{sectionNumber};
            end

        end
        
        function lModelForProfilingTopSettings = ...
                getModelForProfilingTopSettings(this)
            
            lModelForProfilingTopSettings = ...
                this.ModelForProfilingTopSettings;
            
        end
        

        function name = getOriginalComponentName(this)

            name = this.OriginalComponentName;
            
        end
        
        
        function this = ExecTimeData(profileData)

            this.TimerStartValues = profileData.timerValueStart;
            this.NumSamples = length(this.TimerStartValues);
            this.TimerEndValues = profileData.timerValueEnd;
            this.SelfTimerValues = profileData.selfTime;
            this.ExecutionTimeValues = profileData.executionTimeValues;
            if isfield(profileData, 'simTime')
                this.SimulationTimeValues = profileData.simTime;
            end
            this.AnalysisInconsistencies = profileData.analysisInconsistency;

            if isfield(profileData, 'timestampOverhead')
                this.TaskOverhead = profileData.taskOverhead;
                this.FunctionOverhead = profileData.functionOverhead;
                this.TimestampOverhead = profileData.timestampOverhead;
                this.FreezingOverhead = profileData.freezingOverhead;
            end
            
            % Convert from zero-based to one-based
            nodesTmp = profileData.nodes(1:5,:);
            nodesTmp(1,2:end) = nodesTmp(1,2:end)+uint64(1);
            this.Nodes = nodesTmp;
            
            % Convert from zero-based to one-based
            this.NodeIds = profileData.nodeId+uint32(1);

            this.CoreNumStart = profileData.coreNumStart;
            this.CoreNumEnd = profileData.coreNumEnd;
            
            nodes = profileData.nodes;
            sectionIdRowIdx = 2;
            totalTurnaroundTimeRowIdx = 3;
            totalExecutionTimeRowIdx = 4;
            maxTurnaroundTimeRowIdx = 5;
            maxTurnaroundTimeCallNumRowIdx = 6;
            maxExecutionTimeRowIdx = 7;
            maxExecutionTimeCallNumRowIdx = 8;
            totalSelfTimeRowIdx = 9;
            maxSelfTimeRowIdx = 10;
            maxSelfTimeCallNumRowIdx = 11;
            numCallsRowIdx = 12;
            errorCodeRowIdx = 13;
            uniqueIdIdx = 14;
            realTimeNumInconsistenciesIdx = 15;
            realTimeFirstInconsistencyIdx = 16;
            
            this.SectionIds = nodes(sectionIdRowIdx,:)';
            this.TotalTurnaroundTimes = nodes(totalTurnaroundTimeRowIdx,:)';
            this.TotalExecutionTimes = nodes(totalExecutionTimeRowIdx,:)';
            this.MaxTurnaroundTimes = nodes(maxTurnaroundTimeRowIdx,:)';
            this.MaxTurnaroundTimesCallNum = nodes(maxTurnaroundTimeCallNumRowIdx,:)';
            this.MaxExecutionTimes = nodes(maxExecutionTimeRowIdx,:)';
            this.MaxExecutionTimesCallNum = nodes(maxExecutionTimeCallNumRowIdx,:)';
            this.TotalSelfTimes = nodes(totalSelfTimeRowIdx,:)';
            this.MaxSelfTimes = nodes(maxSelfTimeRowIdx,:)';
            this.MaxSelfTimesCallNum = nodes(maxSelfTimeCallNumRowIdx,:)';
            this.NumCalls = nodes(numCallsRowIdx,:)';
            this.ErrorCodes = nodes(errorCodeRowIdx,:)';    
            this.UniqueIds = nodes(uniqueIdIdx,:)';
            this.RealTimeNumInconsistencies = nodes(realTimeNumInconsistenciesIdx,:)';  
            this.RealTimeFirstInconsistency = nodes(realTimeFirstInconsistencyIdx,:)';  
            
            if isfield(profileData, 'TaskSuspendSectionIds')
                this.TaskSuspendSectionIds = profileData.TaskSuspendSectionIds;
            end
           
            this.OverrunTable = profileData.overrunTable;

            % Generate a 64-bit identifier to uniquely identify the data associated
            % with this object
            this.generateIdUint64;
        end
    end
end
