This example shows how to implement a custom mapping algorithm similar to a Simulink mapping mode. It uses the getSlRootInportMap
and getRootInportMap
functions to implement the custom mapping.
This example assumes that you are familiar with the getRootInportMap
command and the Root Inport Mapper Tool custom mapping capability. If you are not familiar with those concepts, read the documentation and review the examples that pertain to getRootInportMap
and custom mappings.
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 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 )
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);
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.
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
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
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')'.