function ltpdasim(block)

%             This is the automatic function wrapper
% ========================================================================
% ==================== level-2 M file S-function =========================
% ========================================================================
% This wrapper is the automatic function, called by every function block
% in Simulink, able to execute m-file functions retrieving from Simulink:
% (1) the pointer to the object(s) to be analyzed, coming in as input of 
%     the corresponding block (ie, the DATA),
% (2) the name of the function to be applied on those data, from the tag
%     of the currently executed block (ie, the true FUNCTION),
% (3) the parameters for that particular block, retrieved from the global
%     shared workspace by the handle of the block (ie, the PARAMETERS).
%
% The output is then generated as:
%    OUTPUT = FUNCTION(DATA,PARAMETERS)
%
% This output in the end is saved into the global array containing all
% the AOs (ie, all the DATA go together with other data): thus this output
% will be freely accessible by all the other functions.
%
% The only real output to Simulink will be just the ordinal number of the
% so-generated AO into the global array of AOs.
%
%   $Id: ltpdasim.m,v 1.37 2008/12/01 20:03:51 nicola Exp $


setup(block);


%% Setup

function setup(block)

  % Register dialog parameter: none, because they're retrieved directly
  % from the memory. This will prevent the user to modify the parameters
  % outside the proper parameters panel:
  block.NumDialogPrms = 0;

  % Register number of input and output ports
  block.NumInputPorts  = 1;
  block.NumOutputPorts = 1;

  % Setup functional port properties to dynamically inherited.
  block.SetPreCompInpPortInfoToDynamic;

  block.InputPort(1).DirectFeedthrough = true;
  block.InputPort(1).DatatypeID = 0;
  block.InputPort(1).Complexity = 0;
  block.OutputPort(1).DatatypeID = 0;
  block.OutputPort(1).Complexity = 0;
  block.SampleTimes = [0 0];
  block.SetAccelRunOnTLC(false);
  
  block.OutputPort(1).Dimensions = 1;
  
  % Register methods
  block.RegBlockMethod('SetInputPortSamplingMode',@SetInpPortFrameData);
  block.RegBlockMethod('SetInputPortDimensions',  @SetInpPortDims);
  block.RegBlockMethod('Outputs',                 @Outputs);
  
  function SetInpPortFrameData(block, idx, fd) %#ok<INUSL>
  block.InputPort(1).SamplingMode = fd;
  block.OutputPort(1).SamplingMode  = fd;
  
  function SetInpPortDims(block, idx, di)
  block.InputPort(idx).Dimensions = di;


%% Outputs
function Outputs(block)
global LTPDAinvar gl loop execModels %#ok<NUSED>

% disp(['The name of the currently executed block is ',get_param(get_param(gcbh,'Parent'),'Name')])
% disp(['The current size of LTPDAinvar is ',num2str(size(LTPDAinvar,1))])

currparent   = get_param(gcbh,'Parent');
subsyshandle = get_param(currparent, 'handle');

% Check if it's a partial execution:
if ismember('executionList',evalin('base','who'))
   executionList = evalin('base','executionList');
   blockIndex = -1;
   for i=1:size(executionList,1), if executionList(i)==gcbh, blockIndex = i; break; end; end
   if blockIndex == -1 % this block is not included in the list of those to be executed.
      disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will be skipped.'])
      block.OutputPort(1).Data = -1;
      return
   else
    % Check if the block must receive data from Simulink or retrieve them from a previous calculation:
      if block.InputPort(1).Data(1)==-1
         annotation  = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
         execHistory = get_param(annotation,'UserData');
         blockIndex = -1;
         for i=1:size(execHistory,1), if execHistory{i,2}==subsyshandle, blockIndex = i; end; end
         if blockIndex == -1 % something's wrong: the selected block is not included in the previous execution history
            warning(['*** Partial execution: impossible to retrieve inputs. The block ',get_param(subsyshandle,'Name'),' seems to have never been executed.'])
            block.OutputPort(1).Data = -1;
            return
         end
         j=3;
         while ~isempty(execHistory{blockIndex,j})
            block.InputPort(1).Data(j-2) = execHistory{blockIndex,j};
            j = j+1;
         end
         disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will now be executed with data retrieved from previous calculation.'])
      else
         disp([' . Partial execution: block ',get_param(subsyshandle,'Name'),' will now be executed.'])
      end
   end
end

% Check if the user wants to stop the execution:
lastChar = get(findobj('Name','LTPDA Progress Bar'),'CurrentCharacter');
if ~isempty(lastChar) && strcmp(lastChar,'x')
    set_param(bdroot, 'SimulationCommand', 'stop')
    return
end

% Update the progress bar window:
  progressBar  = findobj('Tag','progressaxes');
  if ~isempty(progressBar)
     blocksTotal = get(findobj('Tag','blockstotal'),'UserData');
     blocksDone  = get(findobj('Tag','done'),'UserData');
     blocksToGo  = get(findobj('Tag','togo'),'UserData');
     set(findobj('Tag','done'),'UserData',blocksDone+1);
     set(findobj('Tag','togo'),'UserData',blocksToGo-1);
     set(findobj('Tag','done'),'String',['Done: ',num2str(blocksDone+1)]);
     set(findobj('Tag','togo'),'String',['To go: ',num2str(blocksToGo-1)]);
     currblockname = get_param(get_param(gcbh,'Parent'),'Name');
     for i=1:numel(currblockname), if double(currblockname(i))==10, currblockname(i)=' '; end; end
     set(findobj('Tag','currentexec'),'String',['Current block: ',currblockname]);
     progressSize = get(progressBar,'UserData');
     progressPosition    = get(progressBar,'Position');
     progressPosition(3) = 1+(progressSize(3)/blocksTotal)*(blocksDone+1);
     set(progressBar,'Position',progressPosition)
     progressBarLoop  = findobj('Tag','progressaxes2');
     if ~isempty(progressBarLoop)
        set(findobj('Tag','done'),'String',['Blocks done: ',num2str(blocksDone+1)]);
        loopStatus  = getappdata(0,'loopStatus');
        progressSize = get(progressBarLoop,'UserData');
        progressLoopPosition    = get(progressBarLoop,'Position');
        progressLoopPosition(3) = 1+(progressSize(3)/loopStatus(1))*loopStatus(2);
        set(progressBarLoop,'Position',progressLoopPosition)
     end
     drawnow
  end

% Retrieve the function name from the block tag:
  blocktag = get_param(gcb, 'tag');
  if strcmp(blocktag,'generic')
     paramcommand = get_param(get_param(get_param(gcbh,'Parent'), 'handle'),'Description');
     eval(paramcommand)
     blocktag = functionName;
  end
  
% ========================================================================
%% For arithmetic blocks
% ========================================================================

  if strcmp(blocktag,'arithmetic')
      eq = get_param(currparent,'MaskDescription');
      if strcmp(eq,'Arithmetic block: the user can type in the desidered equation.')
          disp(sprintf('Error: in the ''%s'' arithmetic block no equation has been typed in.',get_param(currparent,'Name')))
          close(findobj('Name','LTPDA Progress Bar'))
          error(['Error: in the ',get_param(currparent,'Name'),' arithmetic block no equation has been typed in.'])
      end
      equation = eq;
      equation(strfind(equation,char(10))) = [];
      
      paramcommand = get_param(subsyshandle,'Description');
      if isempty(paramcommand), paramcommand = 'params=plist(''alpha'',''-->'',''beta'',''-->'');'; end
      eval(paramcommand)
      
      for i=1:nparams(params)
         inputBlock = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','Name',lower(params.params(i).key));
         if numel(inputBlock)>1, inputBlock(1)=[]; end % this can happen only when the currparent has the same name: then, ignore the 1st, ie. the currparent itself
         inputNumb  = get_param(inputBlock{1},'Tag');
         equation = strrep(equation,lower(params.params(i).key),['LTPDAinvar{block.InputPort(1).Data(',inputNumb,'),1}']);
      end
      
   try   
      outdata = eval(equation);
      
    % Setting the output signal name:
      ifsetName = get_param(currparent,'UserData');
      if ~isempty(ifsetName) && isa(ifsetName,'char')
        % ifsetName = ifsetName;
      else
          ifsetName = get_param(currparent,'Name');
      end
      if numel(outdata)==1
          outdata = setName(outdata,ifsetName);
      else
          for i=1:size(outdata,1)
              for j=1:size(outdata,2)
                  outdata(i,j) = setName(outdata(i,j),[ifsetName,'_',num2str(i),'-',num2str(j)]);
              end
          end
      end
      
      x = size(LTPDAinvar,1);
      blockName = currparent;
      for i=1:numel(blockName), if double(blockName(i))==10, blockName(i)=' '; end; end
      LTPDAinvar = [LTPDAinvar;{outdata,0,blockName}];
      block.OutputPort(1).Data = x+1;

      probe = get_param(currparent,'MaskHelp');
      if ~isempty(probe) && isa(probe,'char') && strcmp(probe,'probe')
          LTPDAinvar{size(LTPDAinvar,1),2} = 2;
      end
      
      return
      
   catch err
       disp(sprintf('Something''s wrong in the calculation of the results of the ''%s'' arithmetic block.',get_param(currparent,'Name')))
       disp('The inputs were:')
       for i=1:nparams(params)
           disp(['Input ',num2str(i),':'])
           LTPDAinvar{block.InputPort(1).Data(i),1} %#ok<NOPRT>
       end
       disp('The equation was:')
       disp(eq)
       close(findobj('Name','LTPDA Progress Bar'))
       
       rethrow(err)

   end
  end % for the arithmetic block
  
% ========================================================================
%% =================== RETRIEVE THE PARAMETERS LIST ======================
% ========================================================================

  % The command line to produce the proper parameters list for the block
  % currently executed is retrieved from the description of this parent subsystem
    paramcommand = get_param(subsyshandle,'Description');
    loopExecution = 0;

    if ~isempty(paramcommand)
        eval(paramcommand);
        
      % Verify if a global variable or nested loop variable has been used:
        for i=1:nparams(params)
           currVal = params.params(i).val;
            if ischar(currVal) && numel(currVal)>5 && strcmp(currVal(1:5),'loop.')
               loopExecution = 1;
               try %#ok<ALIGN>
                  setVal(params.params(i),eval(params.params(i).val));
               catch, end
            end
            if ischar(currVal) && numel(currVal)>3 && strcmp(currVal(1:3),'gl.')
               try %#ok<ALIGN>
                  setVal(params.params(i),eval(params.params(i).val));
               catch, end
            end
        end
        
        if ~exist('paramEnabled','var'), paramEnabled = ones(1,nparams(params)); end
        currparam = plist();
        for jj=1:nparams(params)
            if paramEnabled(jj)==1
                if numel(params.params(jj).key)>6 && strcmpi(params.params(jj).key(1:7),'addPar_'), params.params(jj).setKey(params.params(jj).key(8:end)); end
                currparam = append(currparam,params.params(jj));
            end
        end
    else
        currparam = plist();
    end

  % Retrieve parameters from Simulink:
    paramFromSimulink = 0;
    for i=1:nparams(currparam)
         paramKey2 = currparam.params(i).key;
         if numel(paramKey2)>7 && strcmpi(paramKey2(1:7),'addPar_'), paramKey = paramKey2(8:end); else paramKey = paramKey2; end
         paramVal = currparam.params(i).val;
         if (isa(paramVal,'char') && strcmp(paramVal,'-->')) || (isa(paramVal,'cell') && numel(paramVal)==1 && isa(paramVal{1},'char') && strcmp(paramVal{1},'-->'))
             paramFromSimulink = paramFromSimulink + 1;
             mux = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','BlockType','Mux');
             muxWidths = get_param(mux{1},'CompiledPortWidths');
             muxWidths = muxWidths.Inport;
             inputBlock = find_system(currparent,'SearchDepth',1,'LookUnderMasks','all','Name',lower(paramKey));
             if numel(inputBlock)>1, inputBlock(1)=[]; end % this can happen only when the currparent has the same name: then, ignore the 1st, ie. the currparent itself
             inputNumb  = str2double(get_param(inputBlock{1},'Tag'));
             x = 0;
             for j=1:inputNumb-1
                 x=x+muxWidths(j);
             end
             paramVal = [LTPDAinvar{block.InputPort(1).Data(x+1)}];
             if muxWidths(inputNumb)>1
                 for j=x+2:x+muxWidths(inputNumb)
                     paramVal = [paramVal,LTPDAinvar{block.InputPort(1).Data(j)}];
                 end
             end
             currparam = pset(currparam,paramKey2,paramVal);
         end
    end
    
% ========================================================================
%% ======================== CALCULATE THE RESULTS =========================
% ========================================================================

   y = length(block.InputPort(1).Data) - paramFromSimulink;

 % Check to verify the type of function:
   specFunc = {'display'};
   [meth,clas] = strtok(get(get_param(currparent,'Handle'),'Tag'));
   category = '';
   if strcmp(meth,'method')
      clas = strtrim(clas);
      infoObj = eval([clas,'.getInfo(''',blocktag,''')']);
      category = infoObj.mcategory;
   end
   
   if ismember(blocktag,specFunc)
     % Input, but no parameters and no output
       type = 4;
     % ======================================
   elseif strcmp(category,'Output')
     % Input and parameters, but no output
       type = 3;
     % ===================================
   elseif ~isempty(utils.prog.find_in_models(currparent,'SearchDepth',1,'LookUnderMasks','all','BlockType','Ground')) || block.InputPort(1).Data(1)==0
     % No input, but parameters and output
       type = 2;
     % ===================================
   elseif nparams(currparam)==0
     % Input and output, but no parameters
       type = 1;
     % ============================
   else
     % Input, parameters and output
       type = 0;
     % ============================
   end


   switch type
     % ====================================================================
       case 4 % Input, but no parameters and no output
            command = 'feval(blocktag,';
            for i=1:y
               dVal = block.InputPort(1).Data(i);
               if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
            end
            command = [command(1:end-1) ');'];
           try
               eval(command)
           catch err
               disp('4')
               disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
               disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
               disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
               close(findobj('Name','LTPDA Progress Bar'))
               rethrow(err)
           end
           block.OutputPort(1).Data = 1; % this will be ignored: no output expected.
           return;

     % ====================================================================
       case 3 % Input and parameters, but no output
           try
            command = 'feval(blocktag,';
            for i=1:y
               dVal = block.InputPort(1).Data(i);
               if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
            end
            command = [command 'currparam);'];
            if strcmpi(blocktag,'plot'), figure; end;
            eval(command);
           catch err
               disp('3')
               disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
               disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
               disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
               disp('The plist passed was:')
               currparam %#ok<NOPRT>
               close(findobj('Name','LTPDA Progress Bar'))
               rethrow(err)
           end
           block.OutputPort(1).Data = 1; % this will be ignored: no output expected.
           return;

     % ====================================================================
       case 2 % No input, but parameters and output
           try
               outdata = feval(blocktag,currparam);
           catch err
               disp('2')
               disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
               disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
               disp('The plist passed was:')
               currparam %#ok<NOPRT>
               close(findobj('Name','LTPDA Progress Bar'))
               rethrow(err)
           end

     % ====================================================================
       case 1 % Input and output, but no parameters
           try
               command = 'outdata = feval(blocktag,';
               for i=1:y
                  dVal = block.InputPort(1).Data(i);
                  if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
               end
               command = [command(1:end-1) ');'];
               eval(command);
           catch err
               disp('1')
               disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
               disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
               disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
               disp('The plist passed was:')
               currparam %#ok<NOPRT>
               close(findobj('Name','LTPDA Progress Bar'))
               rethrow(err)
           end
     % ====================================================================
       case 0 % Input, parameters and output
           try
               command = 'outdata = feval(blocktag,';
               for i=1:y
                  dVal = block.InputPort(1).Data(i);
                  if dVal>0, command = [command sprintf('LTPDAinvar{%d},', dVal)]; end
               end
               command = [command 'currparam);'];
               eval(command);
           catch err
               disp('1')
               disp(sprintf('Something''s wrong in the calculation of the results of the %s function.',blocktag))
               disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
               disp(sprintf('The (first) input data ordinal number, in the list of objects, was %d.',block.InputPort(1).Data(1)))
               disp('The plist passed was:')
               currparam %#ok<NOPRT>
               close(findobj('Name','LTPDA Progress Bar'))
               rethrow(err)
           end

   end

%==========================================================================
%% Cycle to remove objects from memory as soon as they're no longer necessary:
%==========================================================================

if ~getappdata(0,'maintainresults');
   portConnectivity = get(subsyshandle,'PortConnectivity');
   for i = 1:numel(portConnectivity)
      notUsedAnymore = 1;
      if isempty(portConnectivity(i).DstBlock)
         parentBlock = portConnectivity(i).SrcBlock;
         childrenBlocks = utils.prog.findchildren(parentBlock);
         for j = 1:numel(childrenBlocks)
            if isempty(get_param(childrenBlocks(j),'MaskVariables')), notUsedAnymore = 0; end
         end
       % disp(sprintf('*** (Currently executing block %s)',get_param(currparent,'Name')))
         if notUsedAnymore && LTPDAinvar{block.InputPort(1).Data(i),2}==0
         % disp(['*** Removed object n°.',num2str(block.InputPort(1).Data(i)),', which was a ',class(LTPDAinvar{block.InputPort(1).Data(i),1}),'.'])
           disp(sprintf('*** (Currently executing block %s)',get_param(currparent,'Name')))
            LTPDAinvar(block.InputPort(1).Data(i),:) = [];
            
          % Update the execution history:
            ltpda_annotation = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
            execHistory = get_param(ltpda_annotation,'UserData');
            for xx=1:size(execHistory,1)
               for yy=3:size(execHistory,2),
                  if execHistory{xx,yy} == block.InputPort(1).Data(i), execHistory{xx,yy} = -1; end
                  if execHistory{xx,yy} > block.InputPort(1).Data(i), execHistory{xx,yy} = execHistory{xx,yy}-1; end
               end
               if execHistory{xx,1} == block.InputPort(1).Data(i), execHistory{xx,1} = -1; end
               if execHistory{xx,1} > block.InputPort(1).Data(i), execHistory{xx,1} = execHistory{xx,1}-1; end
            end
            set_param(ltpda_annotation,'UserData',execHistory);
            
            disp('*** LTPDA object cleared from memory, being now unused')
         end
      end
   end
end
   % ========================================================================
%% =============================== OUTPUTS ================================
% ========================================================================

  % Set this block status to 'executed':
    set(subsyshandle,'MaskVariables','1');
    set(gcbh,'MaskVariables','1');

signalName = get_param(currparent,'Name');
ifsetName = get_param(currparent,'UserData');
if ~isempty(ifsetName) && isa(ifsetName,'double') && ifsetName==1
    if numel(outdata)==1
        outdata = setName(outdata,signalName);
    else
        for i=1:size(outdata,1)
            for j=1:size(outdata,2)
                outdata(i,j) = set(outdata(i,j),'name',[signalName,'_',num2str(i),'-',num2str(j)]);
            end
        end
    end
end

try
  % To prevent the result calculated here from being canceled at the
  % end of the calculation:
    probe = get_param(currparent,'MaskHelp');
    if ~isempty(probe) && isa(probe,'char') && strcmp(probe,'probe'), keepThis = 1;
    else keepThis = 0;
    end

  % Appending the results:
    ltpda_annotation = find_system(bdroot,'FindAll','on','Type','Annotation','Tag','ltpda model');
    execHistory = get_param(ltpda_annotation,'UserData');
    % If there's a previous execution history, check if the current block belongs to it:
    if ~isempty(execHistory)
       member = 0;
       for i=1:size(execHistory,1)
          if execHistory{i,2}==subsyshandle, member = 1; index = i; break; end
       end
    end
    if ~isempty(execHistory) && member && ~loopExecution
       x = execHistory{index,1};
       if x==-1, x = size(LTPDAinvar,1)+1; end
       for i=1:numel(currparent), if double(currparent(i))==10, currparent(i)=' '; end; end
       LTPDAinvar(x,:) = {outdata,keepThis,currparent};
       block.OutputPort(1).Data = x;
       execHistory{index,1}     = x ;
       execHistory{index,2}     = subsyshandle ;
       for i=1:y , execHistory{index,2+i} = block.InputPort(1).Data(i); end
    else
       if isempty(execHistory), execHistory = {}; end
       x = size(LTPDAinvar,1);
       LTPDAinvar = [LTPDAinvar;{outdata,keepThis,currparent}];
       block.OutputPort(1).Data = x+1;
       execHistory{end+1,1}     = x+1 ;
       execHistory{end,2}       = subsyshandle ;
       for i=1:y , execHistory{end,2+i} = block.InputPort(1).Data(i); end
    end

  % Update the execution history:
    % Are stored the the output obj pointer, the handle of the current subsytem and the input obj pointers:
    set_param(ltpda_annotation,'UserData',execHistory);    
    
    
catch
    disp('------------------============================================------------------')
    disp('------------------============================================------------------')
    disp('------------------============================================------------------')
    disp(sprintf('Something''s wrong in appending to the global variable LTPDAinvar the results of the %s function.',blocktag))
    disp(sprintf('The name of the currently executed block is %s.',get_param(currparent,'Name')))
    disp('The data to be saved, calculated into this block, are:')
    outdata %#ok<NOPRT>
    disp('------------------============================================------------------')
    disp('------------------============================================------------------')
    disp('------------------============================================------------------')
end

%==========================================================================

    % The output to Simulink is just the ordinal number (ONE AND ONLY ONE
    % NUMBER) of the object appended to the global array LTPDAinvar.

% endfunction
