%% Using Mapping Modes with Custom-Mapped External Inputs
%
% This example shows how to implement a custom mapping algorithm similar to
% a Simulink mapping mode. It uses the <docid:simulink_ref#btzccgd-1
% getSlRootInportMap> and <docid:simulink_ref#btji3cb-1 getRootInportMap>
% functions to implement the custom mapping.
% 
% Before running this example, make sure that you are familiar with the
% |getRootInportMap| command and the Root Inport Mapper Tool. For more
% information, see <docid:simulink_ug#btiun7o Map Root Inport Signal Data>.
%

% Copyright 2013-2021 The MathWorks, Inc.

%% Workflow
%
% This example shows how you can use a built-in Simulink mapping mode to
% perform as many mappings as possible. It then flags the root inports that
% were not able to be assigned a signal. The algorithm then overrides the
% flagged mappings with custom mappings to map the remaining signals. To
% implement such a solution, create a custom mapping function using
% the getSlRootInportMap.
%
% This example uses a list of inputs with two kinds of signals:
%
% * Signals that can be mapped using the Simulink block name mapping mode.
% * Signals that cannot be mapped using the Simulink block name mapping
% mode. You must map these signals with a custom mapping mode.
%
% Assume the following scenario: 
% 
% * You want to use a group of signals as inputs to your Simulink model. 
% * The signals are named such that the variable names match the block name of the root-level inport.
% * Each signal that uses this naming convention is within tolerance.
% * Each signal that has the 'x' character appended to its name is considered outside tolerance.
%
% This example uses a mapping mode similar to the Simulink block name mapping method.
% 
% The root-level inport block names are: 
% 
% * Throttle
% * Brake
% 
% The signal variable names are:
% 
% * Throttlex
% * Brake
%
% To map inputs to root-level inport blocks in this scenario, you need a 
% custom mapping function for the Root Inport Mapper tool. This example 
% uses the AlmostBlockName custom mapping function. 
% 
% For this example, you will use the |slexAutotransRootInportsExample| 
% model to validate your custom mapping function. 
% 
% 

%% Declare the Custom Mapping Function
% 
% Declare the function name, inputs, and outputs. To do this, copy 
% and paste the following code snippet into a MATLAB file and save the file
% as AlmostBlockName.m. 
% 
%  function inputMap = AlmostBlockName( modelName, signalNames, signals )

%% Get the Simulink BlockName Mapping
%
% Next, map all the signals. To do this, first map all the signals within
% tolerance using the Simulink block name mapping mode, then map the 
% signals outside tolerance. 
%
% To map signals within tolerance to a model using one of the Simulink mapping modes, use 
% the function |getSlRootInportMap|. This function returns the |inputMap| and
% a vector of logical values. Each logical value indicates a successful or 
% unsuccessful mapping of |inputMap| to a signal. To map by block name, 
% insert the following lines of code just after the function declaration.
% 
%  inputMap = getRootInportMap('empty');
%  if ~bdIsLoaded(modelName)
%     load_system(modelName);
%  end
% 
%  [inputMap, hasASignal] = getSlRootInportMap('Model', modelName, ...
%          'MappingMode','BlockName',...
%          'signalName',signalNames, 'signalValue', signals);

%% Find the Missing Input Signals
%
% In the previous step, you created a mapping using a block name mapping mode. 
% You must now account for an empty |inputMap| and for |inputMap(s)| that were not 
% associated with a signal within tolerance. The function |getSlRootInportMap| 
% has flagged these signals with the output variable |hasASignal|. To do this:  
%
% # Check the |inputMap| variable. 
% # If the |inputMap| variable is not empty, determine which elements of the |inputMap| vector were not assigned a signal. To do this, use the logical ~ on the |hasASignal| vector as shown below. The |emptyIndex| vector now contains a logical vector where true means the |inputMap| does not have a signal mapped to it.
% # Copy and paste the following code snippet under the call to the |getSlRootInportMap| and before the end to the if |bdIsLoaded(modelName)|.
%
%  if ~isempty(inputMap)
%      emptyIndex = ~hasASignal;
%  end
%
%
% The code snippet performs steps one and two for you. 
% 


%% Complete the Mapping
%
% In the previous step, you created a logical vector |emptyIndex| to see if 
% any of the inputMap objects were not associated to a signal. If all the 
% elements of the emptyIndex vector are false, you have a complete mapping 
% and the code added in this section will not be executed. 
% 
% If the |emptyIndex| vector contains at least one value that is true, you 
% have |inputMap| objects that are not associated to a signal. Manually 
% assign the variable signal(s) to that inputMap. Then, override the
% |inputMap| with the signal name that matches the expected signal name:
%
% # In the emptyIndex vector, find all the items that are true. These items point to the |inputMap(s)| that still need to be associated with a signal.  
% # For each |inputMap|, use the 'BlockName' property to get the name of the inport block that the |inputMap| is assigned to. 
% # Append an 'x' to the block name to get the name of the signal to be assigned to the |inputMap|. 
% # Compare the result to each item in the signalNames variable cell array. 
% # If a match is found, override the |inputMap| with the signal name that matches the expected signal name. To override the |inputMap| object, use the getRootInportMap function with the 'InputMap' and the 'SignalName' properties. 
%
%  if isa( signals{1}, 'Simulink.SimulationData.Dataset')
%      signalNames = signals{1}.getElementNames';
%  end
% 
%  idxEmpty = find(emptyIndex==true);
%  for kEmpty =1:length(idxEmpty)
%      idxOfEmpty = idxEmpty(kEmpty);
%      destBlockName = get(inputMap(idxOfEmpty),'BlockName');
%      outSideToleranceSig = [destBlockName 'x'];
%      isAMatch = strcmp(signalNames, outSideToleranceSig);
%      if any(isAMatch)
%          inputMap(idxOfEmpty) = getRootInportMap('InputMap', ...
%              inputMap(idxOfEmpty),'SignalName',outSideToleranceSig);
%      end
%  end

%% The Custom Map File
%
% When you are done, the file AlmostBlockName.m should resemble the following code.
%
%  function inputMap = AlmostBlockName(modelName, signalNames, signals)
%  inputMap = getRootInportMap('empty');
%  if bdIsLoaded(modelName)
%
%       [inputMap, hasASignal] = getSlRootInportMap('Model', modelName, ...
%       'MappingMode','BlockName',...
%       'signalName',signalNames, 'signalValue', signals);
%
%       if ~isempty(inputMap)
%          emptyIndex = ~hasASignal;        
%          idxEmpty = find(emptyIndex==1);
%
%          if isa( signals{1}, 'Simulink.SimulationData.Dataset')
%               signalNames = signals{1}.getElementNames';
%          end          
%
%          for kEmpty =1:length(idxEmpty)
%              idxOfEmpty = idxEmpty(kEmpty);            
%              destBlockName = get(inputMap(idxOfEmpty),'BlockName');
%              nonNominalSig = [destBlockName 'x'];
%              isAMatch = strcmp(signalNames, nonNominalSig);
%              if any(isAMatch)
%                  inputMap(idxOfEmpty) = getRootInportMap('InputMap', ...
%                      inputMap(idxOfEmpty),'SignalName',nonNominalSig);
%              end
%          end
%        
%       end
%  end

%% Validate the Custom Mapping
%
% To validate your custom mapping:
% 
% # Save the AlmostBlockName function in a file on the MATLAB path. 
% # To see the results of your mapping function, copy and paste the following code snippet to the MATLAB Command Window.
% 
%  modelName  = 'slexAutotransRootInportsExample';
%  Throttlex  = timeseries(zeros(10,1));
%  Brake      = timeseries(ones(10,1));
%  signalNames= {'Throttlex' ,'Brake'};
%  signals    = { Throttlex  , Brake };
%  open_system(modelName);
%  inputMap   = AlmostBlockName(modelName, signalNames, signals);
%  inputStr   = getInputString(inputMap,'base');
%  close_system(modelName);
%
% After running the code snippet, the variable |inputStr| contains the string 'Throttlex,Brake'.
%
%
% If your signals are in a Simulink.SimulationData.Dataset, to see the 
% results of your mapping function, use the following code snippet at the MATLAB Command Window.
%
%  modelName  = 'slexAutotransRootInportsExample';
%  Throttlex  = timeseries(zeros(10,1));
%  Brake      = timeseries(ones(10,1));
%  ds         = Simulink.SimulationData.Dataset;
%  ds         = ds.addElement( Throttlex, 'Throttlex' );
%  ds         = ds.addElement( Brake, 'Brake' );
%  signalNames= {'ds'};
%  signals    = { ds };
%  open_system(modelName);
%  inputMap   = AlmostBlockName(modelName, signalNames, signals);
%  inputStr   = getInputString(inputMap,'base');
%  close_system(modelName);
%
% After running the code snippet for signals in a Simulink.SimulationData.Dataset, 
% the variable |inputStr| contains the string 'ds.getElement('Throttlex'),ds.getElement('Brake')'.

