Skip to content

Commit e59dbd0

Browse files
Merge pull request #1 from McSCert/subsys-to-simfunc
Functionality to convert a Subsystem to a Simulink Function
2 parents 18d2c7d + 621c4af commit e59dbd0

File tree

5 files changed

+322
-0
lines changed

5 files changed

+322
-0
lines changed

src/isGoodSimFcnName.m

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
function goodName = isGoodSimFcnName(subsystem, simulinkFcnName)
2+
% isGoodSimFcnName Checks to see if a Simulink Function name is the
3+
% same as any other Simulink Functions in scope at
4+
% the current subsystem level
5+
% Inputs:
6+
% subsystem Path of a subsystem where a Simulink Function
7+
% will be added
8+
% simulinkFcnName Name of the Simulink Function to be added
9+
%
10+
% Outputs:
11+
% goodName Name can be used(1) or not(0)
12+
%
13+
% Example:
14+
% isGoodSimFcnName('SubSystem_Name', 'SimFcnName')
15+
%
16+
% ans = 1
17+
18+
% Get callable Simulink Functions at the current scope
19+
[~, prototype] = getCallableFunctions(subsystem);
20+
21+
% Get the prototype names
22+
prototypeNames = getPrototypeName(prototype);
23+
24+
% Make sure the prototype name is a cell
25+
if ~iscell(prototypeNames)
26+
prototypeNames = {prototypeNames};
27+
end
28+
29+
result = zeros(1, length(prototypeNames));
30+
31+
% Loop through each prototype name
32+
for name = 1:length(prototypeNames)
33+
% Compare to the input name
34+
result(name) = ~strcmp(simulinkFcnName, prototypeNames{name});
35+
end
36+
% Return true if none of the prototype names are the same as the input name
37+
goodName = all(result);
38+
end

src/isSubsystem.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function b = isSubsystem(blocks)
2+
% ISSUBSYSTEM Determine if the block is a subsystem.
3+
%
4+
% Inputs:
5+
% block Pathname or handle of a block.
6+
%
7+
% Outputs:
8+
% b Whether the block is a subsystem(1) or not(0).
9+
10+
% Convert the input to handles
11+
blocks = inputToNumeric(blocks);
12+
13+
b = zeros(1, length(blocks));
14+
% Check each block to see if its a subsystem
15+
for block = 1:length(blocks)
16+
try
17+
blockType = get_param(blocks(block), 'BlockType');
18+
b(block) = strcmpi(blockType, 'SubSystem');
19+
catch % Unexpected error
20+
end
21+
end
22+
end

src/reqSimFcnName.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function simulinkFcnName = reqSimFcnName()
2+
% getSimFcnName Prompts the user for an input Simulink Function name
3+
% until a name is entered which is not the same as
4+
% another Simulink Function in scope
5+
%
6+
% Inputs:
7+
% N/A
8+
%
9+
% Outputs:
10+
% simulinkFcnName Char array which represents a Simulink Function name
11+
%
12+
% Example:
13+
% simulinkFcnName = getSimFcnName()
14+
%
15+
% ans = 'Function_Name'
16+
17+
%% Dialog Box Parameters
18+
prompt = 'Enter a name for the Simulink Function: ';
19+
dlgtitle = 'Convert Subsystem';
20+
dims = [1 50];
21+
definput = {'f'};
22+
23+
%% Checking Input
24+
% Loop until the input name is acceptable
25+
while 1
26+
inputName = inputdlg(prompt, dlgtitle, dims, definput);
27+
% Checks to see if the name shadows other names in scope
28+
if isGoodSimFcnName(gcs, inputName{1})
29+
break
30+
else
31+
waitfor(msgbox([inputName{1}, ...
32+
' is already used as a Simulink Function in scope.', ...
33+
newline, newline, 'Please enter a new name.'], dlgtitle));
34+
end
35+
end
36+
simulinkFcnName = inputName{1};
37+
end

src/sl_customization.m

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,44 @@ function sl_customization(cm)
1616
elseif any(selectedFcns) && ~isempty(gcbs)
1717
schemaFcns{end+1} = @ChangeFcnScopeSchema;
1818
schemaFcns{end+1} = @FcnCreatorLocalSchema;
19+
elseif isSubsystem(gcbs)
20+
schemaFcns{end+1} = @ConvToSimFcnSchema;
1921
end
2022
end
2123

24+
%% Define action: Convert Subsystem to Simulink Function
25+
function schema = ConvToSimFcnSchema(callbackInfo)
26+
schema = sl_container_schema;
27+
schema.label = 'Convert Subsystem';
28+
schema.ChildrenFcns = {@toScopedSimFcn, @toGlobalSimFcn};
29+
end
30+
31+
function schema = toScopedSimFcn(callbackInfo)
32+
schema = sl_action_schema;
33+
schema.label = 'To Scoped Simulink Function';
34+
schema.userdata = 'toScopedSimFcn';
35+
schema.callback = @toScopedSimFcnCallback;
36+
end
37+
38+
function toScopedSimFcnCallback(callbackInfo)
39+
simulinkFcnName = reqSimFcnName();
40+
subsystem = gcbs;
41+
subToSimFcn(subsystem{1}, simulinkFcnName, 'scoped');
42+
end
43+
44+
function schema = toGlobalSimFcn(callbackInfo)
45+
schema = sl_action_schema;
46+
schema.label = 'To Global Simulink Function';
47+
schema.userdata = 'toGlobalSimFcn';
48+
schema.callback = @toGlobalSimFcnCallback;
49+
end
50+
51+
function toGlobalSimFcnCallback(callbackInfo)
52+
simulinkFcnName = reqSimFcnName();
53+
subsystem = gcbs;
54+
subToSimFcn(subsystem{1}, simulinkFcnName, 'global');
55+
end
56+
2257
%% Define action: Create Function Caller for Local Function
2358
function schema = FcnCreatorLocalSchema(callbackInfo)
2459
schema = sl_action_schema;

src/subToSimFcn.m

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
function subToSimFcn(subsystem, simulinkFcnName, visibility)
2+
% subToSimFunc Converts a subsystem to a Simulink Function
3+
%
4+
% Inputs:
5+
% subsystem Path of a subsystem to be converted
6+
% simulinkFcnName Name of the Simulink Function to be created
7+
% visibility Set function visibility to'scoped' or 'global'
8+
%
9+
% Outputs:
10+
% N/A
11+
%
12+
% Example:
13+
% subToSimFunc('Demo_Example/f_Sensor_Trip_1', 'f_Sensor_Trip_i', 'scoped')
14+
%
15+
% Converts 'f_Sensor_Trip_1' subsystem to a
16+
% scoped Simulink Function 'f_SensorTrip_i'
17+
18+
%% Input Validation
19+
20+
% Check that the subsystem is loaded
21+
try
22+
assert(ischar(subsystem));
23+
assert(bdIsLoaded(bdroot(subsystem)));
24+
catch
25+
error('Invalid subsystem. Model may not be loaded or name is invalid.');
26+
end
27+
28+
% Check that the function name is valid
29+
try
30+
assert(isvarname(simulinkFcnName));
31+
catch
32+
error('Invalid function name. Use a valid MATLAB variable name.');
33+
end
34+
35+
% Check that the function visibility is valid
36+
try
37+
assert(strcmp(visibility, 'scoped') || strcmp(visibility, 'global'));
38+
catch
39+
error('Invalid function visibility. Use scoped/global visibility.');
40+
end
41+
42+
%% Add Trigger to Subsystem
43+
44+
% Break library link
45+
set_param(subsystem, 'LinkStatus', 'none');
46+
47+
% Adding the trigger block to the subsystem and calibrating its parameters
48+
triggerPath = append(subsystem, '/', simulinkFcnName);
49+
add_block('simulink/Ports & Subsystems/Trigger', triggerPath);
50+
set_param(triggerPath, 'TriggerType', 'function-call', ...
51+
'IsSimulinkFunction', 'on', 'FunctionName', simulinkFcnName, ...
52+
'FunctionVisibility', visibility);
53+
54+
% Set subsystem to atomic execution
55+
set_param(subsystem, 'TreatAsAtomicUnit', 'on');
56+
57+
%% Convert Inports to ArgIns
58+
59+
% Create array of all the inports in the subsystem
60+
allInports = find_system(subsystem, 'SearchDepth', 1, ...
61+
'BlockType', 'Inport');
62+
63+
% Getting the parameters for all inports in the subsystem
64+
inportParameters = getPortParameters(allInports);
65+
66+
% Replace inports with argument inputs
67+
replace_block(subsystem, 'SearchDepth', 1, ...
68+
'BlockType', 'Inport', 'ArgIn', 'noprompt');
69+
70+
% Create array of all the argIns in the Simulink Function
71+
allArgIns = find_system(subsystem, 'SearchDepth', 1, 'BlockType', 'ArgIn');
72+
73+
% Setting the parameters for all the argument inputs
74+
setArgumentParameters(allArgIns, inportParameters);
75+
76+
%% Convert Outports to ArgOuts
77+
78+
% Create array of all the outports in the subsystem
79+
allOutports = find_system(subsystem, 'SearchDepth', 1, ...
80+
'BlockType', 'Outport');
81+
82+
% Getting the parameters for all outports in the subsystem
83+
outportParameters = getPortParameters(allOutports);
84+
85+
% Replace outports with argument outputs
86+
replace_block(subsystem, 'SearchDepth', 1, ...
87+
'BlockType', 'Outport', 'ArgOut', 'noprompt');
88+
89+
% Create array of all the argOuts for the Simulink Function
90+
allArgOuts = find_system(subsystem, 'SearchDepth', 1, ...
91+
'BlockType', 'ArgOut');
92+
93+
% Setting the parameters for all the argument outputs
94+
setArgumentParameters(allArgOuts, outportParameters);
95+
end
96+
97+
function parameters = getPortParameters(ports)
98+
% getPortParameters Returns the parameters for an inport or outport
99+
%
100+
% Inputs:
101+
% ports Cell array of inports or outports
102+
%
103+
% Outputs:
104+
% parameters Cell array of parameters including:
105+
% 1) Port
106+
% 2) OutMin
107+
% 3) OutMax
108+
% 4) OutDataTypeStr
109+
% 5) LockScale
110+
% 6) PortDimensions
111+
% 7) ArgumentName
112+
%
113+
% Example:
114+
% parameters = getPortParameters({'System/Subsystem/Inport1'})
115+
%
116+
% ans =
117+
% 1x7 cell array
118+
% {'1'} {'[]'} {'[]'} {'boolean'} {'off'} {'on'} {'Inport1'}
119+
120+
%% Get the port parameters
121+
% Init array of parameters for the ports
122+
parameters = cell(length(ports), 7);
123+
% Loop through each port
124+
for port = 1:length(ports)
125+
% Get the port name by splitting the pathname by the backslash
126+
splitPortPath = split(ports{port}, '/');
127+
% The port name is the last index of splitPortPath
128+
portName = splitPortPath{end};
129+
% Get the port number
130+
parameters{port, 1} = get_param(ports{port}, 'Port');
131+
% Get the port minimum output
132+
parameters{port, 2} = get_param(ports{port}, 'OutMin');
133+
% Get the port maximum output
134+
parameters{port, 3} = get_param(ports{port}, 'OutMax');
135+
% Get the port data type
136+
parameters{port, 4} = get_param(ports{port}, 'OutDataTypeStr');
137+
% If data type is set to inherit, set to double by default
138+
try
139+
assert(not(strcmp(parameters{port, 4}, 'Inherit: auto')));
140+
catch
141+
disp([portName, ...
142+
' data type was set to ''Inherit: auto''', ...
143+
' - setting to ''double''...']);
144+
parameters{port, 4} = 'double';
145+
end
146+
% Get the port lock scale
147+
parameters{port, 5} = get_param(ports{port}, 'LockScale');
148+
% Get the port dimension
149+
parameters{port, 6} = get_param(ports{port}, 'PortDimensions');
150+
% If port dimension is set to inherit, set to 1 by default
151+
try
152+
assert(not(strcmp(parameters{port, 6}, '-1')))
153+
catch
154+
disp([portName, ...
155+
' dimension was set to ''-1''', ...
156+
' - setting to ''1''...']);
157+
parameters{port, 6} = '1';
158+
end
159+
% Remove spaces from port name to create a valid variable name
160+
parameters{port, 7} = genvarname(portName);
161+
end
162+
end
163+
164+
function setArgumentParameters(arguments, parameters)
165+
% setArgumentParameters Sets argIn or ArgOut parameters
166+
%
167+
% Inputs:
168+
% arguments Cell array of argIns or argOuts
169+
% parameters Cell array of parameters for each argument
170+
%
171+
% Outputs:
172+
% N/A
173+
%
174+
% Example:
175+
% setArgumentParameters({'System/Subsystem/argIn1'}, ...
176+
% {'1', '[]', '[]', 'boolean', 'off', 'on', 'Inport1'})
177+
178+
%% Set the argument parameters
179+
% Loop through each argument
180+
for arg = 1:length(arguments)
181+
% Set the parameters for each argument
182+
set_param(arguments{arg}, 'Port', parameters{arg, 1}, ...
183+
'OutMin', parameters{arg, 2}, ...
184+
'OutMax', parameters{arg, 3}, ...
185+
'OutDataTypeStr', parameters{arg, 4}, ...
186+
'LockScale', parameters{arg, 5}, ...
187+
'PortDimensions', parameters{arg, 6}, ...
188+
'ArgumentName', parameters{arg, 7});
189+
end
190+
end

0 commit comments

Comments
 (0)