Skip to content

Commit

Permalink
Merge pull request #12 from McSCert/10-stateflow-support
Browse files Browse the repository at this point in the history
Closes #10 Stateflow support added
  • Loading branch information
monikajaskolka committed Apr 3, 2022
2 parents 6bbc0f4 + 0ce5db6 commit e2409bd
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 21 deletions.
Binary file modified doc/ModelComparisonUtility_UserGuide.pdf
Binary file not shown.
3 changes: 0 additions & 3 deletions doc/tex/ModelComparisonUtility_UserGuide.tex
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,6 @@ \subsection{Highlighting Nodes in the Model}
>> highlightNodes(added, mdl2);
\end{lstlisting}

\section{Limitations}
Support for Stateflow comparison will be added in a future release of this tool.

\begin{thebibliography}{9}
\bibitem{CompareXML}
The MathWorks.
Expand Down
16 changes: 15 additions & 1 deletion src/find_node.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
% pairs (not case-sensitive, except for NodeName). The following describes
% the available constraint/value pairs:
%
% NodeType ['block' | 'line' | 'port' | 'annotation' | 'mask' | 'block_diagram' | ...]
% NodeType ['block' | 'line' | 'port' | 'annotation' | 'mask' | 'block_diagram' | 'stateflow' | ...]
% ChangeType ['added' | 'deleted' | 'modified' | 'renamed' | 'none']
% BlockType ['SubSystem' | 'Inport' | 'Outport' | ...]
% StateflowType ['Stateflow.Annotation' | 'Sateflow.Transition' | 'Sateflow.State' | ...]
% NodeName <Node.Name>
% FirstOnly [('off'), 'on'] For changes with 2 nodes (e.g., none, modified),
% returns the first 'before' node. This is
Expand Down Expand Up @@ -107,6 +108,7 @@
nodeType = lower(getInput('NodeType', varargin));
changeType = lower(getInput('ChangeType', varargin));
blockType = lower(getInput('BlockType', varargin));
stateflowType = lower(getInput('StateflowType', varargin));
nameValue = getInput('NodeName', varargin);

% Check against varargin constraints
Expand Down Expand Up @@ -148,6 +150,18 @@
end
end

if ~isempty(stateflowType) && meetsConstraints
if iscell(stateflowType)
isStateflowType = ismember(stateflowType, lower(getStateflowType(node)));
else
isStateflowType = strcmpi(stateflowType, getStateflowType(node));
end

if ~any(isStateflowType)
meetsConstraints = false;
end
end

if ~isempty(nameValue) && meetsConstraints
if iscell(nameValue)
isMatchedName = ismember(nameValue, node.Name);
Expand Down
9 changes: 8 additions & 1 deletion src/getHandle.m
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,13 @@
hdl = ports(i);
return
end
end
end
elseif strcmp(type, 'stateflow')
p = getPath(node, sys);
if ~isempty(p)
hdl = get_param(p, 'Handle');
else
hdl = [];
end
end
end
7 changes: 5 additions & 2 deletions src/getPath.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function path = getPath(node, sys)
% GETPATH Get the path of a node in a model. Note: Not all elements in a
% model have a Path parameter (e.g. lines, annotations). If an empty cell
% array is returned, no valid path has been found.
% model have a Path parameter (e.g. lines, annotations, Sateflow transitions).
% If an empty cell array is returned, no valid path has been found.
%
% Inputs:
% node xmlcomp.Node object, representing a block.
Expand All @@ -24,6 +24,9 @@
end

sys = char(sys);
if isStateflow(node)
node = getStateflowParent(node);
end
path = assemblePath(sys, node);

% Address R2016b bug
Expand Down
90 changes: 90 additions & 0 deletions src/getStateflowObj.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
function obj = getStateflowObj(node)
% GETSTATEFLOWOBJ Get the model element corresponding to the Stateflow
% node. Stateflow objects don't have handles like Simulink, therefore this
% function returns the object itself. Note: The model needs to be open.
%
% Inputs:
% node xmlcomp.Node object.
%
% Outputs:
% obj Stateflow object.

obj = '';
chart_node = getStateflowParent(node);
chart = find(sfroot, '-isa', 'Stateflow.Chart', 'Name', chart_node.Name);

for i = 1:length(chart) % Multiple charts with the same name can be found, so search all

if ~isempty(node.Parameters)
%% Get type from model by following the SSID, name, or label string
paramNames = {node.Parameters.Name};
paramVals = {node.Parameters.Value};
idx_id = find(strcmpi(paramNames, 'ID'));
idx_ssid = find(strcmpi(paramNames, 'SSID'));
idx_lbl = find(strcmpi(paramNames, 'labelString'));
idx_name = find(strcmpi(paramNames, 'Name'));
idx_text = find(strcmp(paramNames, 'Text'));
idx_port = find(strcmp(paramNames, 'Port'));
idx_block = find(strcmp(paramNames, 'BlockType'));

if numel(idx_name) > 1 % If both 'name' and 'Name' found, use 'Name'
idx_name = find(strcmp(paramNames, 'Name'));
end

obj = '';
if node == chart_node
% Node is the chart itself
obj = chart(i);
elseif ~isempty(idx_id)
% Try to find and object with the same SSID
obj = find(chart(i), 'ssid', str2num(paramVals{idx_id}));
elseif ~isempty(idx_ssid)
obj = find(chart(i), 'ssid', str2num(paramVals{idx_ssid}));
elseif ~isempty(idx_lbl)
obj = find(chart(i), 'LabelString', paramVals{idx_lbl});
elseif ~isempty(idx_name) && (isempty(idx_port) && isempty(idx_block))
obj = find(chart(i), '-isa', 'Stateflow.Data', 'Name', paramVals{idx_name});
elseif ~isempty(idx_name) && ~isempty(idx_port)
obj = find(chart(i), '-isa', 'Stateflow.Data', 'Name', paramVals{idx_name});
elseif ~isempty(idx_name)
name = paramVals{idx_name};
obj = find(chart(i), 'Name', name);
if isempty(obj)
% Inports/outports may have () appended
name_stripped = regexprep(name, '\(.*?\)', '');
obj = find(chart(i), 'Name', name_stripped);
end
if isempty(obj)
% May be a function
obj = find(chart(i), 'LabelString', name);
end
elseif ~isempty(idx_text)
% Check if annotation by comparing text
objs = find(chart(i), '-isa', 'Stateflow.Annotation');
for j = 1:length(objs)
if contains(objs(j).Text, paramVals{idx_text})
obj = objs(j);
break;
end
end
end
end

if isempty(obj)
%% Get object from the node name only
if isempty(node.Name)
% Check if name matches name
obj = find(chart(i), '-not', '-isa', 'Stateflow.Chart', 'Name', node.Name);

% Check if label matches name
if isempty(obj)
obj = find(chart(i), 'LabelString', node.Name);
end
end
end

if ~isempty(obj)
break;
end
end
end
25 changes: 25 additions & 0 deletions src/getStateflowParent.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function parent = getStateflowParent(node)
% GETSTATEFLOWPARENT Get the parent node of the Stateflow object node.
%
% Inputs:
% node xmlcomp.Node object.
%
% Outputs:
% parent Parent node.

parent = node;
while hasParent(parent)
if ~isempty(parent.Parameters)
paramNames = {parent.Parameters.Name};
paramVals = {parent.Parameters.Value};
idx = find(contains(paramNames, 'SFBlockType'));
if ~isempty(idx) && contains(paramVals{idx}, {'Chart', 'State Transition Table', 'Truth Table'})
break
else
parent = parent.Parent;
end
else
parent = parent.Parent;
end
end
end
18 changes: 16 additions & 2 deletions src/highlight/highlightNodes.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,26 @@ function highlightNodes(nodes, sys, varargin)
bgColor = 'yellow';
end

% If given Nodes instead of handles, get the handles
% If given Nodes instead of handles, get the handles and/or Stateflow
% objects
if isa(nodes, 'xmlcomp.Node')
hdls = zeros(1, length(nodes));
sfObj = [];
for i = 1:length(nodes)
try
hdls(i) = getHandle(nodes(i), sys);
catch
hdls(i) = NaN;
end

if isStateflow(nodes(i))
sfObj = vertcat(sfObj, getStateflowObj(nodes(i)));
end
end
nodes = hdls(isfinite(hdls(:))); % Remove invalid hdls
end

% Highlight
% %Highlight Simulink
if method
colorRegular(nodes, fgColor, bgColor);
else
Expand All @@ -87,6 +93,14 @@ function highlightNodes(nodes, sys, varargin)
'BackgroundColor', bgColor));
hilite_system_notopen(nodes, 'user2');
end

%% Highlight Stateflow
% Limitation: Only one Stateflow object can be highlighted at once.
% This will attempt to highlight all successively, but only the last
% one will appear.
for i = 1:numel(sfObj)
highlight(sfObj(i));
end
end

function colorRegular(hdls, fg, bg)
Expand Down
32 changes: 22 additions & 10 deletions src/plot/editsToDigraph.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
D.Nodes.Label = nodes;
end

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

% Handle input
omitParam = lower(getInput('OmitParam', varargin, 'on'));

% Initialize
source = {};
target = {};
nodes = {};

% Add the root node
source{end+1} = 'Edits';
nodes{end+1} = 'Edits';

source{end+1} = 'Edits';
target{end+1} = 'Comparison Root (before)';
target{end+1} = 'Comparison Root (after)';
nodes{end+1} = 'Edits';
nodes{end+1} = 'Comparison Root (before}';

source{end+1} = 'Edits';
target{end+1} = 'Comparison Root (after)';
nodes{end+1} = 'Comparison Root (after}';

% Before
Expand All @@ -60,9 +66,12 @@
suffix = ' (before)';

for k = 1:length(children)
source{end+1} = [parent suffix];
target{end+1} = [getPathTree(children(k)) suffix];
nodes{end+1} = char(children(k).Name);
if strcmp(omitParam, 'off') || ...
(strcmp(omitParam, 'on') && (~strcmp(getNodeType(children(k)), 'unknown') || children(k).Edited == 1))
source{end+1} = [parent suffix];
target{end+1} = [getPathTree(children(k)) suffix];
nodes{end+1} = char(children(k).Name);
end
end
end
end
Expand All @@ -79,9 +88,12 @@
suffix = ' (after)';

for k = 1:length(children)
source{end+1} = [parent suffix];
target{end+1} = [getPathTree(children(k)) suffix];
nodes{end+1} = char(children(k).Name);
if strcmp(omitParam, 'off') || ...
(strcmp(omitParam, 'on') && (~strcmp(getNodeType(children(k)), 'unknown') || children(k).Edited == 1))
source{end+1} = [parent suffix];
target{end+1} = [getPathTree(children(k)) suffix];
nodes{end+1} = char(children(k).Name);
end
end
end
end
Expand Down
29 changes: 29 additions & 0 deletions src/type/isStateflow.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function out = isStateflow(node)
% ISSTATEFLOW Determine if the node represents a Stateflow object.
%
% Inputs:
% node xmlcomp.Node object.
%
% Outputs:
% out Whether the node represents an Stateflow object(1) or not(0).

out = false;

% Inspect all parents of the node to see if it is contained in a Stateflow
% Chart or Stateflow table.
while hasParent(node)
if ~isempty(node.Parameters)
paramNames = {node.Parameters.Name};
paramVals = {node.Parameters.Value};
idx = find(contains(paramNames, 'SFBlockType'));
if ~isempty(idx) && contains(paramVals{idx}, {'Chart', 'State Transition Table', 'Truth Table'})
out = true;
break
else
node = node.Parent;
end
else
node = node.Parent;
end
end
end
8 changes: 6 additions & 2 deletions src/type/nodetype/getNodeType.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function type = getNodeType(node, varargin)
% GETNODETYPE Determine the type of element that the node represents in the
% model: block, block_diagram, line, port, mask, or annotation.
% model: block, block_diagram, line, port, mask, annotation, or stateflow.
%
% In general, only elements which are Edited can have their type inferred
% directly from the tree. For those that cannot be inferred, if sys is
Expand Down Expand Up @@ -44,7 +44,11 @@

% First try to see if we can check the type without looking at the model.
% If that does not work, check the model
if ~isempty(node.Parameters) && any(strcmp({node.Parameters.Name}, 'BlockType'))
if isStateflow(node)
type = 'stateflow';
elseif strcmp(node.Name, 'Comparison Root') && isempty(node.Parent)
type = 'unknown'; % Tree artifact
elseif ~isempty(node.Parameters) && any(strcmp({node.Parameters.Name}, 'BlockType'))
type = 'block';
elseif isBlockDiagram(node)
type = 'block_diagram';
Expand Down

0 comments on commit e2409bd

Please sign in to comment.