classdef (StrictDefaults) MPGSysObj < matlab.System
   % System object for MultiPlot Graph block
   % NOTE: A new instance is created whenever editing the block dialog
   % so don't assume this remains the same object for the lifetime of 
   % the block.

   %  Copyright 2022 The MathWorks, Inc.

   % Public, non-tunable properties
   properties (Nontunable)
      % tmin
      tmin (1,1) double
      % tmax
      tmax (1,1) double 
      % ymin
      ymin double
      % ymax
      ymax double
      % Plot title
      PlotTitle char;
      % Workspace variable for export
      ExportVariable char
   end

   properties (DiscreteState)

   end

   properties (Access = protected)
      PlotManager = [];
   end

   methods
      % Constructor
      function obj = MPGSysObj()
         %disp('Constructing new instance')
      end
   end

   methods (Access = protected)
      %% Common functions
      function sts = getSampleTimeImpl(obj)
         sts = createSampleTime(obj, 'Type', 'Inherited');
      end

      function inputName = getInputNamesImpl(~)
         inputName = '';
      end

      function num = getNumInputsImpl(~)
         % Define total number of inputs
         num = 1;
      end

      function num = getNumOutputsImpl(~)
         % Define total number of outputs
         num = 0;
      end

      function setupImpl(obj)
         % Perform one-time initialization based on system object settings
         bh = gcbh;
         mblk = get_param(get_param(get(bh,'Parent'),'Parent'),'Parent');
         % Get number of inputs
         cpd = get(bh,'CompiledPortDimensions');
         if size(cpd.Inport,1)==1 && cpd.Inport(1,1)==1
            ny = cpd.Inport(1,2);
         else
            error(message('Robust:simulink:MultiPlot5'))
         end
         % Resolve signal names
         YLabels = get_param(mblk,'InputSignalNames');
         if all(cellfun(@isempty,YLabels))
            YLabels = cellstr("Input "+(1:ny));
         elseif ny>1 && isscalar(YLabels)
            YLabels = cellstr(YLabels{1}+"("+(1:ny)+")");
         end
         % Get plot manager ready
         hPM = get_param(bh,'UserData');
         if isempty(hPM) || ~isConfigured(hPM,ny)
            hPM = rctMultiPlotManager(bh,mblk,ny,YLabels);
            set_param(bh,'UserData',hPM)
         end
         % Cross-link System object and plot manager
         hPM.SystemObject = obj;
         obj.PlotManager = hPM;
         % Prepare new plot
         initializeSim(hPM)
      end

      function stepImpl(obj,y)
         % Gets called at each major time step
         if ~obj.isMinorTimeStep
            t = getCurrentTime(obj);
            logSample(obj.PlotManager,t,y)
         end
      end

      function releaseImpl(obj)
         % Cleanup upon termination
         logData(obj.PlotManager)
      end

      function validatePropertiesImpl(obj)
         % Validate related or interdependent property values
         if obj.tmin>=obj.tmax
            error(message('Robust:simulink:MultiPlot2'))
         end
         if ~(isscalar(obj.ymin) || isscalar(obj.ymax) || numel(obj.ymin)==numel(obj.ymax))
            error(message('Robust:simulink:MultiPlot4'))
         elseif any(obj.ymin>=obj.ymax)
            error(message('Robust:simulink:MultiPlot3'))
         end
      end

      %% Simulink functions
      function ds = getDiscreteStateImpl(obj)
         % Return structure of properties with DiscreteState attribute
         ds = struct([]);
      end

      function flag = isInputSizeMutableImpl(obj,index)
         % Return false if input size cannot change
         % between calls to the System object
         flag = false;
      end

      function icon = getIconImpl(~)
         % Define icon for System block
         icon = mfilename("class"); 
      end
   end

   methods (Static, Access = protected)
      %% Simulink customization functions
      function header = getHeaderImpl
         % Define header panel for System block dialog
         txt = getString(message('Robust:simulink:MultiPlotDesc')); 
         header = matlab.system.display.Header('Title','MultiPlot Graph',...
            'Text',txt,'ShowSourceLink',false);
      end

      function simMode = getSimulateUsingImpl
         % Return only allowed simulation mode in System block dialog
         simMode = "Interpreted execution";
      end
   end
end
