Skip to content

Commit e2409bd

Browse files
Merge pull request #12 from McSCert/10-stateflow-support
Closes #10 Stateflow support added
2 parents 6bbc0f4 + 0ce5db6 commit e2409bd

11 files changed

+216
-21
lines changed
-1.25 KB
Binary file not shown.

doc/tex/ModelComparisonUtility_UserGuide.tex

-3
Original file line numberDiff line numberDiff line change
@@ -482,9 +482,6 @@ \subsection{Highlighting Nodes in the Model}
482482
>> highlightNodes(added, mdl2);
483483
\end{lstlisting}
484484

485-
\section{Limitations}
486-
Support for Stateflow comparison will be added in a future release of this tool.
487-
488485
\begin{thebibliography}{9}
489486
\bibitem{CompareXML}
490487
The MathWorks.

src/find_node.m

+15-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
% pairs (not case-sensitive, except for NodeName). The following describes
1919
% the available constraint/value pairs:
2020
%
21-
% NodeType ['block' | 'line' | 'port' | 'annotation' | 'mask' | 'block_diagram' | ...]
21+
% NodeType ['block' | 'line' | 'port' | 'annotation' | 'mask' | 'block_diagram' | 'stateflow' | ...]
2222
% ChangeType ['added' | 'deleted' | 'modified' | 'renamed' | 'none']
2323
% BlockType ['SubSystem' | 'Inport' | 'Outport' | ...]
24+
% StateflowType ['Stateflow.Annotation' | 'Sateflow.Transition' | 'Sateflow.State' | ...]
2425
% NodeName <Node.Name>
2526
% FirstOnly [('off'), 'on'] For changes with 2 nodes (e.g., none, modified),
2627
% returns the first 'before' node. This is
@@ -107,6 +108,7 @@
107108
nodeType = lower(getInput('NodeType', varargin));
108109
changeType = lower(getInput('ChangeType', varargin));
109110
blockType = lower(getInput('BlockType', varargin));
111+
stateflowType = lower(getInput('StateflowType', varargin));
110112
nameValue = getInput('NodeName', varargin);
111113

112114
% Check against varargin constraints
@@ -148,6 +150,18 @@
148150
end
149151
end
150152

153+
if ~isempty(stateflowType) && meetsConstraints
154+
if iscell(stateflowType)
155+
isStateflowType = ismember(stateflowType, lower(getStateflowType(node)));
156+
else
157+
isStateflowType = strcmpi(stateflowType, getStateflowType(node));
158+
end
159+
160+
if ~any(isStateflowType)
161+
meetsConstraints = false;
162+
end
163+
end
164+
151165
if ~isempty(nameValue) && meetsConstraints
152166
if iscell(nameValue)
153167
isMatchedName = ismember(nameValue, node.Name);

src/getHandle.m

+8-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,13 @@
207207
hdl = ports(i);
208208
return
209209
end
210-
end
210+
end
211+
elseif strcmp(type, 'stateflow')
212+
p = getPath(node, sys);
213+
if ~isempty(p)
214+
hdl = get_param(p, 'Handle');
215+
else
216+
hdl = [];
217+
end
211218
end
212219
end

src/getPath.m

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
function path = getPath(node, sys)
22
% GETPATH Get the path of a node in a model. Note: Not all elements in a
3-
% model have a Path parameter (e.g. lines, annotations). If an empty cell
4-
% array is returned, no valid path has been found.
3+
% model have a Path parameter (e.g. lines, annotations, Sateflow transitions).
4+
% If an empty cell array is returned, no valid path has been found.
55
%
66
% Inputs:
77
% node xmlcomp.Node object, representing a block.
@@ -24,6 +24,9 @@
2424
end
2525

2626
sys = char(sys);
27+
if isStateflow(node)
28+
node = getStateflowParent(node);
29+
end
2730
path = assemblePath(sys, node);
2831

2932
% Address R2016b bug

src/getStateflowObj.m

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
function obj = getStateflowObj(node)
2+
% GETSTATEFLOWOBJ Get the model element corresponding to the Stateflow
3+
% node. Stateflow objects don't have handles like Simulink, therefore this
4+
% function returns the object itself. Note: The model needs to be open.
5+
%
6+
% Inputs:
7+
% node xmlcomp.Node object.
8+
%
9+
% Outputs:
10+
% obj Stateflow object.
11+
12+
obj = '';
13+
chart_node = getStateflowParent(node);
14+
chart = find(sfroot, '-isa', 'Stateflow.Chart', 'Name', chart_node.Name);
15+
16+
for i = 1:length(chart) % Multiple charts with the same name can be found, so search all
17+
18+
if ~isempty(node.Parameters)
19+
%% Get type from model by following the SSID, name, or label string
20+
paramNames = {node.Parameters.Name};
21+
paramVals = {node.Parameters.Value};
22+
idx_id = find(strcmpi(paramNames, 'ID'));
23+
idx_ssid = find(strcmpi(paramNames, 'SSID'));
24+
idx_lbl = find(strcmpi(paramNames, 'labelString'));
25+
idx_name = find(strcmpi(paramNames, 'Name'));
26+
idx_text = find(strcmp(paramNames, 'Text'));
27+
idx_port = find(strcmp(paramNames, 'Port'));
28+
idx_block = find(strcmp(paramNames, 'BlockType'));
29+
30+
if numel(idx_name) > 1 % If both 'name' and 'Name' found, use 'Name'
31+
idx_name = find(strcmp(paramNames, 'Name'));
32+
end
33+
34+
obj = '';
35+
if node == chart_node
36+
% Node is the chart itself
37+
obj = chart(i);
38+
elseif ~isempty(idx_id)
39+
% Try to find and object with the same SSID
40+
obj = find(chart(i), 'ssid', str2num(paramVals{idx_id}));
41+
elseif ~isempty(idx_ssid)
42+
obj = find(chart(i), 'ssid', str2num(paramVals{idx_ssid}));
43+
elseif ~isempty(idx_lbl)
44+
obj = find(chart(i), 'LabelString', paramVals{idx_lbl});
45+
elseif ~isempty(idx_name) && (isempty(idx_port) && isempty(idx_block))
46+
obj = find(chart(i), '-isa', 'Stateflow.Data', 'Name', paramVals{idx_name});
47+
elseif ~isempty(idx_name) && ~isempty(idx_port)
48+
obj = find(chart(i), '-isa', 'Stateflow.Data', 'Name', paramVals{idx_name});
49+
elseif ~isempty(idx_name)
50+
name = paramVals{idx_name};
51+
obj = find(chart(i), 'Name', name);
52+
if isempty(obj)
53+
% Inports/outports may have () appended
54+
name_stripped = regexprep(name, '\(.*?\)', '');
55+
obj = find(chart(i), 'Name', name_stripped);
56+
end
57+
if isempty(obj)
58+
% May be a function
59+
obj = find(chart(i), 'LabelString', name);
60+
end
61+
elseif ~isempty(idx_text)
62+
% Check if annotation by comparing text
63+
objs = find(chart(i), '-isa', 'Stateflow.Annotation');
64+
for j = 1:length(objs)
65+
if contains(objs(j).Text, paramVals{idx_text})
66+
obj = objs(j);
67+
break;
68+
end
69+
end
70+
end
71+
end
72+
73+
if isempty(obj)
74+
%% Get object from the node name only
75+
if isempty(node.Name)
76+
% Check if name matches name
77+
obj = find(chart(i), '-not', '-isa', 'Stateflow.Chart', 'Name', node.Name);
78+
79+
% Check if label matches name
80+
if isempty(obj)
81+
obj = find(chart(i), 'LabelString', node.Name);
82+
end
83+
end
84+
end
85+
86+
if ~isempty(obj)
87+
break;
88+
end
89+
end
90+
end

src/getStateflowParent.m

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function parent = getStateflowParent(node)
2+
% GETSTATEFLOWPARENT Get the parent node of the Stateflow object node.
3+
%
4+
% Inputs:
5+
% node xmlcomp.Node object.
6+
%
7+
% Outputs:
8+
% parent Parent node.
9+
10+
parent = node;
11+
while hasParent(parent)
12+
if ~isempty(parent.Parameters)
13+
paramNames = {parent.Parameters.Name};
14+
paramVals = {parent.Parameters.Value};
15+
idx = find(contains(paramNames, 'SFBlockType'));
16+
if ~isempty(idx) && contains(paramVals{idx}, {'Chart', 'State Transition Table', 'Truth Table'})
17+
break
18+
else
19+
parent = parent.Parent;
20+
end
21+
else
22+
parent = parent.Parent;
23+
end
24+
end
25+
end

src/highlight/highlightNodes.m

+16-2
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,26 @@ function highlightNodes(nodes, sys, varargin)
6464
bgColor = 'yellow';
6565
end
6666

67-
% If given Nodes instead of handles, get the handles
67+
% If given Nodes instead of handles, get the handles and/or Stateflow
68+
% objects
6869
if isa(nodes, 'xmlcomp.Node')
6970
hdls = zeros(1, length(nodes));
71+
sfObj = [];
7072
for i = 1:length(nodes)
7173
try
7274
hdls(i) = getHandle(nodes(i), sys);
7375
catch
7476
hdls(i) = NaN;
7577
end
78+
79+
if isStateflow(nodes(i))
80+
sfObj = vertcat(sfObj, getStateflowObj(nodes(i)));
81+
end
7682
end
7783
nodes = hdls(isfinite(hdls(:))); % Remove invalid hdls
7884
end
7985

80-
% Highlight
86+
% %Highlight Simulink
8187
if method
8288
colorRegular(nodes, fgColor, bgColor);
8389
else
@@ -87,6 +93,14 @@ function highlightNodes(nodes, sys, varargin)
8793
'BackgroundColor', bgColor));
8894
hilite_system_notopen(nodes, 'user2');
8995
end
96+
97+
%% Highlight Stateflow
98+
% Limitation: Only one Stateflow object can be highlighted at once.
99+
% This will attempt to highlight all successively, but only the last
100+
% one will appear.
101+
for i = 1:numel(sfObj)
102+
highlight(sfObj(i));
103+
end
90104
end
91105

92106
function colorRegular(hdls, fg, bg)

src/plot/editsToDigraph.m

+22-10
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
D.Nodes.Label = nodes;
2222
end
2323

24-
function [source, target, nodes] = createSourceTarget(root)
24+
function [source, target, nodes] = createSourceTarget(root, varargin)
2525
% CREATESOURCETARGET Get the directed graph edges as (source, target) pairs for
2626
% a comparison tree.
2727
% (See www.mathworks.com/help/matlab/ref/digraph.html#mw_26035adf-ff90-4a33-a8f8-42048d7e39a6)
@@ -34,17 +34,23 @@
3434
% target Cell array of target nodes.
3535
% nodes Cell array of node labels.
3636

37+
% Handle input
38+
omitParam = lower(getInput('OmitParam', varargin, 'on'));
39+
40+
% Initialize
3741
source = {};
3842
target = {};
3943
nodes = {};
4044

4145
% Add the root node
42-
source{end+1} = 'Edits';
46+
nodes{end+1} = 'Edits';
47+
4348
source{end+1} = 'Edits';
4449
target{end+1} = 'Comparison Root (before)';
45-
target{end+1} = 'Comparison Root (after)';
46-
nodes{end+1} = 'Edits';
4750
nodes{end+1} = 'Comparison Root (before}';
51+
52+
source{end+1} = 'Edits';
53+
target{end+1} = 'Comparison Root (after)';
4854
nodes{end+1} = 'Comparison Root (after}';
4955

5056
% Before
@@ -60,9 +66,12 @@
6066
suffix = ' (before)';
6167

6268
for k = 1:length(children)
63-
source{end+1} = [parent suffix];
64-
target{end+1} = [getPathTree(children(k)) suffix];
65-
nodes{end+1} = char(children(k).Name);
69+
if strcmp(omitParam, 'off') || ...
70+
(strcmp(omitParam, 'on') && (~strcmp(getNodeType(children(k)), 'unknown') || children(k).Edited == 1))
71+
source{end+1} = [parent suffix];
72+
target{end+1} = [getPathTree(children(k)) suffix];
73+
nodes{end+1} = char(children(k).Name);
74+
end
6675
end
6776
end
6877
end
@@ -79,9 +88,12 @@
7988
suffix = ' (after)';
8089

8190
for k = 1:length(children)
82-
source{end+1} = [parent suffix];
83-
target{end+1} = [getPathTree(children(k)) suffix];
84-
nodes{end+1} = char(children(k).Name);
91+
if strcmp(omitParam, 'off') || ...
92+
(strcmp(omitParam, 'on') && (~strcmp(getNodeType(children(k)), 'unknown') || children(k).Edited == 1))
93+
source{end+1} = [parent suffix];
94+
target{end+1} = [getPathTree(children(k)) suffix];
95+
nodes{end+1} = char(children(k).Name);
96+
end
8597
end
8698
end
8799
end

src/type/isStateflow.m

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
function out = isStateflow(node)
2+
% ISSTATEFLOW Determine if the node represents a Stateflow object.
3+
%
4+
% Inputs:
5+
% node xmlcomp.Node object.
6+
%
7+
% Outputs:
8+
% out Whether the node represents an Stateflow object(1) or not(0).
9+
10+
out = false;
11+
12+
% Inspect all parents of the node to see if it is contained in a Stateflow
13+
% Chart or Stateflow table.
14+
while hasParent(node)
15+
if ~isempty(node.Parameters)
16+
paramNames = {node.Parameters.Name};
17+
paramVals = {node.Parameters.Value};
18+
idx = find(contains(paramNames, 'SFBlockType'));
19+
if ~isempty(idx) && contains(paramVals{idx}, {'Chart', 'State Transition Table', 'Truth Table'})
20+
out = true;
21+
break
22+
else
23+
node = node.Parent;
24+
end
25+
else
26+
node = node.Parent;
27+
end
28+
end
29+
end

src/type/nodetype/getNodeType.m

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function type = getNodeType(node, varargin)
22
% GETNODETYPE Determine the type of element that the node represents in the
3-
% model: block, block_diagram, line, port, mask, or annotation.
3+
% model: block, block_diagram, line, port, mask, annotation, or stateflow.
44
%
55
% In general, only elements which are Edited can have their type inferred
66
% directly from the tree. For those that cannot be inferred, if sys is
@@ -44,7 +44,11 @@
4444

4545
% First try to see if we can check the type without looking at the model.
4646
% If that does not work, check the model
47-
if ~isempty(node.Parameters) && any(strcmp({node.Parameters.Name}, 'BlockType'))
47+
if isStateflow(node)
48+
type = 'stateflow';
49+
elseif strcmp(node.Name, 'Comparison Root') && isempty(node.Parent)
50+
type = 'unknown'; % Tree artifact
51+
elseif ~isempty(node.Parameters) && any(strcmp({node.Parameters.Name}, 'BlockType'))
4852
type = 'block';
4953
elseif isBlockDiagram(node)
5054
type = 'block_diagram';

0 commit comments

Comments
 (0)