├── .gitignore
├── LICENSE
├── README.md
├── doc
├── ModelComparisonUtility_UserGuide.pdf
├── figs
│ ├── ComparisonTree.pdf
│ ├── McSCert_Logo.pdf
│ ├── ObjectTable.png
│ ├── Plot.pdf
│ └── example
│ │ ├── After.pdf
│ │ ├── Before.pdf
│ │ ├── ExampleModel.pptx
│ │ ├── First.png
│ │ ├── Second.png
│ │ ├── Second_After.png
│ │ ├── Third.png
│ │ └── Third_After.png
└── tex
│ ├── ModelComparisonUtility_UserGuide.tex
│ └── macros.tex
├── example
├── demo_after.mdl
└── demo_before.mdl
├── imgs
├── Cover.png
├── Cover.svg
├── Cover_MathWorks.png
└── SocialPreview.png
└── src
├── Utility
├── Branch.m
├── getInput.m
├── getNameFromPath.m
└── iscellcell.m
├── find_node.m
├── getBranch.m
├── getFile.m
├── getHandle.m
├── getNode.m
├── getParentPath.m
├── getPath.m
├── getPathTree.m
├── getStateflowObj.m
├── getStateflowParent.m
├── getSubTree.m
├── hasChildren.m
├── hasParent.m
├── highlight
├── highlightNodes.m
└── hilite_system_notopen.m
├── isChildrenModified.m
├── nodecmp.m
├── plot
├── editsToDigraph.m
└── plotTree.m
├── printChildren.m
├── printParams.m
├── summaryOfChanges.m
├── treeDepth.m
├── treeNumNodes.m
├── treeToTable.m
└── type
├── blocktype
└── getNodeBlockType.m
├── changetype
├── getNodeChangeType.m
├── isAdded.m
├── isDeleted.m
├── isModified.m
└── isRenamed.m
├── isInport.m
├── isOutport.m
├── isStateflow.m
├── isSubsystem.m
└── nodetype
├── getNodeType.m
├── isAnnotation.m
├── isBlock.m
├── isBlockDiagram.m
├── isConfiguration.m
├── isLine.m
├── isMask.m
└── isPort.m
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.asv
3 | *.aux
4 | *.log
5 | *.out
6 | *.gz
7 | *.toc
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2022, McSCert
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Model Comparison Utility
2 | [](https://doi.org/10.5281/zenodo.4321649)
3 | [](https://www.mathworks.com/matlabcentral/fileexchange/71834-model-comparison-utility)
4 | #### The Model Comparison Utility provides command line functions for supporting Simulink model comparison.
5 |
6 |
7 |
8 | Differencing between two models is natively supported in Simulink via the [Simulink Comparison Tool](https://www.mathworks.com/help/simulink/model-comparison.html). This tool can generate a Word or HTML report displaying the changes that occur between models. Unfortunately, for large industrial models, these generated reports are not readable. As an alternative, the tool can output the comparison results to the Matlab base workspace as an `EditsObj` object that is structured as a tree.
9 |
10 | Unfortunately, MathWorks provides no built-in commands to be able to easily and programmatically query or parse this tree from the command line or a script. Manually doing so for industrial models is simply not possible. Moreover, extracting information from the tree requires thorough knowledge of the tree structure and the object parameters, and thus is not trivial without much effort. The Model Comparison Utility was created to facilitate such operations via a collection of commands. Some useful commands provided by this tool are:
11 |
12 | * `find_node` - Search the comparison tree for nodes with specific block types, changes, names, etc.
13 | * `getHandle` - Get the handle of the model element associated with the node from the comparison tree.
14 | * `getPath` - Get the pathname of the model element associated with the node from the comparison tree.
15 | * `getNodeType` - Determine whether the node presents a block, line, annotation, mask, etc.
16 | * `plotTree` - Plot the digraph of the comparison tree.
17 | * `treeToTable` - Convert the comparison tree to a table.
18 | * `summaryOfChanges` - Print a summary report of the changes in the comparison tree to the Command Window or a .txt file.
19 |
20 | Many other commands are included and are free to be used, but are not listed here. Please explore the source files for this utility to see all the of the various functions.
21 |
22 | ## User Guide
23 | For installation and other information, please see the [User Guide](doc/ModelComparisonUtility_UserGuide.pdf).
24 |
--------------------------------------------------------------------------------
/doc/ModelComparisonUtility_UserGuide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/ModelComparisonUtility_UserGuide.pdf
--------------------------------------------------------------------------------
/doc/figs/ComparisonTree.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/ComparisonTree.pdf
--------------------------------------------------------------------------------
/doc/figs/McSCert_Logo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/McSCert_Logo.pdf
--------------------------------------------------------------------------------
/doc/figs/ObjectTable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/ObjectTable.png
--------------------------------------------------------------------------------
/doc/figs/Plot.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/Plot.pdf
--------------------------------------------------------------------------------
/doc/figs/example/After.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/After.pdf
--------------------------------------------------------------------------------
/doc/figs/example/Before.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/Before.pdf
--------------------------------------------------------------------------------
/doc/figs/example/ExampleModel.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/ExampleModel.pptx
--------------------------------------------------------------------------------
/doc/figs/example/First.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/First.png
--------------------------------------------------------------------------------
/doc/figs/example/Second.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/Second.png
--------------------------------------------------------------------------------
/doc/figs/example/Second_After.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/Second_After.png
--------------------------------------------------------------------------------
/doc/figs/example/Third.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/Third.png
--------------------------------------------------------------------------------
/doc/figs/example/Third_After.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/doc/figs/example/Third_After.png
--------------------------------------------------------------------------------
/doc/tex/ModelComparisonUtility_UserGuide.tex:
--------------------------------------------------------------------------------
1 | \documentclass{article}
2 |
3 | %%%%%%%%%%%%%%%%%%%%%%%%%
4 | % Packages & Macros
5 | %%%%%%%%%%%%%%%%%%%%%%%%%
6 |
7 | % For including graphics
8 | \usepackage{graphicx}
9 |
10 | % For title page
11 | \usepackage{datetime}
12 | \newdateformat{monthyeardate}{\monthname[\THEMONTH] \THEYEAR}
13 |
14 | % Needed above hyperref for \footnote to link to the right spot
15 | \usepackage[symbol]{footmisc}
16 |
17 | % For supporting linking
18 | \usepackage{hyperref}
19 | \hypersetup{colorlinks,urlcolor=blue,linkcolor=blue}
20 |
21 | % For table colouring (in command line tables)
22 | \usepackage{colortbl}
23 |
24 | % For supporting subfigure captions
25 | \usepackage{subcaption}
26 |
27 | % For code formatting
28 | \usepackage{xcolor}
29 | \usepackage{listings}
30 |
31 | %%%%%%%%%%%%%%%%%%%%%%%%%
32 | % Tool-Specific Macros
33 | %%%%%%%%%%%%%%%%%%%%%%%%%
34 | \input{macros}
35 |
36 | \newcommand{\ToolName}{Model Comparison Utility\@\xspace}
37 |
38 | \newcommand{\func}[1]{%
39 | \ifthenelse{\equal{#1}{1}}{\keyword{find\_node}}{}%
40 | \ifthenelse{\equal{#1}{2}}{\keyword{getHandle}}{}%
41 | \ifthenelse{\equal{#1}{3}}{\keyword{getPath}}{}%
42 | \ifthenelse{\equal{#1}{4}}{\keyword{plotTree}}{}%
43 | \ifthenelse{\equal{#1}{5}}{\keyword{summaryOfChanges}}{}%
44 | \ifthenelse{\equal{#1}{6}}{\keyword{getPathTree}}{}%
45 | \ifthenelse{\equal{#1}{7}}{\keyword{highlightNodes}}{}%
46 | }
47 |
48 | \lstdefinestyle{code}{
49 | frame=single,
50 | basicstyle=\ttfamily\small,
51 | keywordstyle=\color{blue},
52 | identifierstyle=\color{black},
53 | stringstyle=\color{violet},
54 | commentstyle=\color{green!40!black},
55 | numberstyle=\color[rgb]{0.411, 0.411, 0.411},
56 | language=Matlab,
57 | keepspaces=false,
58 | columns=flexible,
59 | breaklines=true,
60 | numbers=left,
61 | deletekeywords={line},
62 | morekeywords=[2]{slxmlcomp, compare, load_system, open_system},
63 | keywordstyle=[2]{\color{blue}},
64 | morekeywords=[3]{find_node, getHandle, getPath, getPathTree, plotTree, summaryOfChanges, highlightNodes},
65 | keywordstyle=[3]{\color{orange}},
66 | }
67 | \lstset{style=code}
68 |
69 | %%%%%%%%%%%%%%%%%%%%%%%%%
70 | % Text Macros
71 | %%%%%%%%%%%%%%%%%%%%%%%%%
72 |
73 | \newcommand{\EditsObj}{\keyword{xmlcomp.Edits}\@\xspace}
74 | \newcommand{\NodeObj}{\keyword{xmlcomp.Node}\@\xspace}
75 |
76 | %%%%%%%%%%%%%%%%%%%%%%%%%
77 | % Document
78 | %%%%%%%%%%%%%%%%%%%%%%%%%
79 |
80 | \title{\ToolName}
81 | \date{\monthyeardate\today}
82 |
83 | \begin{document}
84 |
85 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 | % Title Page
87 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 | \maketitle
89 | \vfill
90 |
91 | \begin{figure}
92 | \centering
93 | \includegraphics[]{../figs/McSCert_Logo.pdf} \\
94 | McMaster Centre for Software Certification (McSCert)
95 | \end{figure}
96 |
97 | \newpage
98 |
99 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100 | % Table of Contents
101 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 |
103 | \tableofcontents
104 | \newpage
105 |
106 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107 | % Introduction
108 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109 | \section{Introduction}
110 | % Briefly, what is the tool?
111 |
112 | Differencing between two models is natively supported in Simulink via the \href{https://www.mathworks.com/help/simulink/model-comparison.html}{Simulink Comparison Tool}. This tool relies on XML comparison techniques to generate a Word or HTML report displaying the changes that occur between models. Unfortunately, for large industrial models, these generated reports are not readable. As an alternative, the tool can output the comparison results to the \Matlab base workspace as an \EditsObj object that is structured as a tree. An example of this object's structure can be seen in Figure~\ref{FIG:tree}.
113 |
114 | Unfortunately, MathWorks provides no built-in commands to be able to easily and programmatically query or parse this tree from the command line or a script. Manually doing so for industrial models is simply not possible. Moreover, extracting information from the tree requires thorough knowledge of the tree structure and the object parameters, and thus is not trivial without much effort. The \ToolName was created to facilitate such operations via a collection of commands. Some useful commands provided by this tool are:
115 |
116 | \begin{itemize}
117 | \item \func{1} -- Search the comparison tree for nodes with specific block types, changes, names, \etc~(Section~\ref{SEC:findnode}).
118 | \item \func{2} -- Get the handle of the model element associated with the node from the comparison tree~(Section~\ref{SEC:gethandle}).
119 | \item \func{3} -- Get the pathname of the model element associated with the node from the comparison tree~(Section~\ref{SEC:getpath}).
120 | \item \func{6} -- Get the path of the tree element in the comparison tree~(Section~\ref{SEC:getpathtree}).
121 | \item \func{4} -- Plot the digraph of the comparison tree~(Section~\ref{SEC:plot}).
122 | \item \func{5} -- Print a summary report of the changes in the comparison tree to the Command Window or a \file{.txt} file~(Section~\ref{SEC:summary}).
123 | \item \func{7} -- Highlight model elements corresponding to comparison tree nodes~(Section~\ref{SEC:highlight}).
124 | \end{itemize}
125 | Many other commands are included and are free to be used, but are not listed here. Please explore the source files for this utility to see all the of the various functions.
126 |
127 | \subsection{About the Comparison Tree}
128 |
129 | This section gives a brief overview of how a comparison tree is structured and explains some of the unintuitive aspects of the tree. A graphical representation of a comparison tree is shown in Figure~\ref{FIG:tree}. Note that there are slight differences to this tree between some \Simulink versions (\eg between R2016b, R2017b, and R2019a).
130 |
131 | In general, there are two kinds of objects that comprise the tree: \EditsObj and \NodeObj. The root node of a comparison tree is an \EditsObj object. It is shown in black in Figure~\ref{FIG:tree}. This object contains information about the comparison, including file names of the files being compared, filters applied during comparison, and most importantly, the hierarchical nodes that differ between the two models. The \EditsObj properties are described in Figure~\ref{FIG:ObjectTable}. The \param{LeftRoot} and \param{RightRoot} link to the \NodeObj objects that make up each sub-tree representing each model.
132 |
133 | \begin{figure}[!htb]
134 | \centering
135 | \includegraphics[width=\textwidth,]{../figs/ObjectTable}
136 | \caption{Properties of the comparison objects~\cite{CompareXML}.}
137 | \label{FIG:ObjectTable}
138 | \end{figure}
139 |
140 | Each element that is an \NodeObj usually represents a block, line, annotation, port, mask, or block diagram from the \Simulink model that has been changed. \Simulink model elements which have \emph{not} been changed are not included in the tree, unless they are a componentization block, such as a \subsystem, that is needed to preserve the hierarchy (\eg \block{Subsystem1} and \block{Subsystem2} in Figure~\ref{FIG:tree}. It is important to remember that the tree is only representative of the parts of the model that have changed, and unchanged parts of the model may not be represented by the tree.
141 |
142 | An \NodeObj object's properties are described in Figure~\ref{FIG:ObjectTable}. Note that the \param{Edited} field is only set to \textit{true} when the node itself is different in the tree hierarchy (deleted, added, moved). It is not set when a node property (block parameter) is changed. The \ToolName does not rely on this field as an accurate indicator of change in the model. To determine the types of changes that occur, the \ToolName takes into account changes to the node's \param{Name}, \param{Partner}, \param{Parent}, and \param{Parameters}. Moreover, the placement in the tree is important for understanding how the element has changed, as summarized in Table~\ref{TBL:Placement}.
143 |
144 | \begin{table}[htb]
145 | \centering
146 | \begin{tabular}{lp{26em}}
147 | \hline
148 | Change Type & \NodeObj Placement in the Comparison Tree\\ \hline \hline
149 | Added & Node exists in the right sub-tree, but not the left \\ \hline
150 | Deleted & Node exists in the left sub-tree, but not the right \\ \hline
151 | Modified & Node exists in the left and right sub-trees, and it is partnered \\ \hline
152 | Renamed & Node exists in the left and right sub-trees, and it is not partnered \\ \hline
153 | \end{tabular}
154 | \caption{Effect of placement on an \NodeObj}
155 | \label{TBL:Placement}
156 | \end{table}
157 |
158 | It is important to look at the whole tree to understand the changes. By only looking at the left sub-tree, a node that exists in the left sub-tree can be either deleted or renamed (but definitely not added or modified). By only looking at the right-subtree, a node that exists in the right-subtree can be added or modified (but definitely not deleted). Even after determining the placement of the node, it is important to check its other properties (and that of its partner) to further understand what type of change it has experienced. The \ToolName performs all of these checks automatically, so the user does not have to.
159 |
160 | % Is there more information?
161 | \subsection{More Information}
162 | For more information about model comparison with \Matlab/\Simulink, please see the MathWorks documentation: \newline
163 | \url{https://www.mathworks.com/help/simulink/model-comparison.html}
164 |
165 | \pagebreak
166 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167 | % How to Use the Tool
168 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169 | \section{How to Use the Tool}
170 | This section describes what must be done to setup the tool, as well as how to use the tool.
171 |
172 | %---------------------------------------
173 | % What needs to be done before the tool can be used?
174 | % What needs to be done to a model in order for it to work on said model?
175 | %---------------------------------------
176 | \subsection{Prerequisites and Installation}
177 |
178 | \begin{enumerate}
179 | \item Use \Matlab/\Simulink R2016b or newer.
180 | \begin{itemize}
181 | \item Note: R2016b has many bugs in the model comparison algorithm. For best results, please use R2019a+.
182 | \end{itemize}
183 | \item To install the tool, use one of the following approaches:
184 | \begin{enumerate}
185 | \item \textbf{Download the \file{.zip} from GitHub}
186 | \begin{enumerate}
187 | \item Unzip the contents into your desired location.
188 | \item Add the unzipped folder and subfolders to your \mpath.
189 | \item Download the \href{https://github.com/McSCert/Simulink-Utility}{Simulink-Utility} in the same manner. Add the folder and subfolders to your \mpath also. This is a dependency for the tool to work correctly.
190 | \end{enumerate}
191 | \item \textbf{Use the Git command line}
192 | \begin{enumerate}
193 | \item Use the following command to download the tool and any necessary submodules.
194 | \begin{verbatim}
195 | git clone --recursive https://github.com/McSCert/Model-Comparison-Utility
196 | \end{verbatim}
197 | \item Add the folder and subfolders to your \mpath.
198 | \end{enumerate}
199 | \item \textbf{If you already have the files}
200 | \begin{enumerate}
201 | \item Add the tool folder and subfolders to your \mpath.
202 | \end{enumerate}
203 | \end{enumerate}
204 | \item Run \href{https://www.mathworks.com/help/simulink/ug/registering-customizations.html}{sl\_refresh\_customizations} to refresh the Context Menu.
205 | \item Ensure your model is open and unlocked.
206 | \end{enumerate}
207 |
208 | \paragraph{Troubleshooting:} If running the command ``\cmd{which find\_node}'' indicates that the script is not found, then the tool needs to be added to the \mpath. For information on adding files to the \mpath, please see the \href{https://www.mathworks.com/help/matlab/matlab_env/add-remove-or-reorder-folders-on-the-search-path.html}{MathWorks documentation}.
209 | %---------------------------------------
210 | % How/when do you access the tool?
211 | %---------------------------------------
212 | \subsection{Getting Started}
213 |
214 | The utility commands are used via the \Matlab Command Window. To compare models and create a comparison tree, use the \cmd{slxmlcomp.compare} function by entering:
215 |
216 | \begin{lstlisting}
217 | Edits = slxmlcomp.compare(model1, model2)
218 | \end{lstlisting}
219 |
220 | \noindent where \cmd{model1} is the model before changes, and \cmd{model2} is the model after changes. Two example models, \file{demo\_before.mdl} and \file{demo\_after.mdl}, are provided.
221 |
222 | \begin{lstlisting}
223 | Edits = slxmlcomp.compare('demo_before', 'demo_after')
224 | \end{lstlisting}
225 |
226 | The \EditsObj object is a root node that links to two n-ary sub-trees of differences between two models. The comparison tree for the example is shown in Figure~\ref{FIG:tree}. The nodes in blue correspond to actual elements in the \Simulink models.
227 |
228 | %trim={ }
229 | \begin{figure}[!htb]
230 | \centering
231 | \includegraphics[width=\textheight, angle=90]{../figs/ComparisonTree}
232 | \caption{The \EditsObj object as created by the \Simulink Comparison Tool.}
233 | \label{FIG:tree}
234 | \end{figure}
235 |
236 | %---------------------------------------
237 | % What are the main uses of the tool?
238 | %---------------------------------------
239 | \subsection{Functionality}
240 | \label{SEC:functionality}
241 | This section describes a few of the useful functions that are provided by the \ToolName. Full instructions on function parameters and output are given in the scripts' header comments. Feel free to explore all the scripts that are included! An example of using these functions is given in Section~\ref{SEC:example}.
242 |
243 | \subsubsection{Finding Nodes}
244 | \label{SEC:findnode}
245 | A comparison tree can be comprised of numerous nodes. The \func{1} function lets you search the tree for a specific node, in a similar way that the \href{https://www.mathworks.com/help/simulink/slref/find_system.html}{\cmd{find\_system}} function searches for elements in a \Simulink model. The user can provide a list of constraints, and the function will return all nodes that fit them. It is possible to search for a node based on its change type (\eg added, deleted, modified, renamed), block type (\subsystem, \inport, \constant, \etc), block name, or node name.
246 |
247 | \subsubsection{Getting a Node's Handle in the Model}
248 | \label{SEC:gethandle}
249 | One of the issues with the comparison tree is that although \NodeObj objects abstractly represent elements from the \Simulink models from which it was generated, there is no built-in way of getting a node's handle. The \func{2} function will return the node's handle, if one exists. Note that some objects do not have an associated handle in the model (\eg Mask, Comparison Root), while other objects have two handles if they exist in both sub-trees (\eg renamed block).
250 |
251 | \subsubsection{Getting a Node's Path in the Model}
252 | \label{SEC:getpath}
253 | The \func{3} function returns the node's full pathname in the model.
254 | Be aware that some model elements do not have a path (\eg lines, annotations).
255 | Note, that if the path of the node in the tree is desired, please use the \cmd{getPathTree} function.
256 |
257 | \subsubsection{Getting a Node's Path in the Comparison Tree}
258 | \label{SEC:getpathtree}
259 | The \func{6} function returns the node's full path in the comparison tree.
260 |
261 | \subsubsection{Plotting the Comparison Tree}
262 | \label{SEC:plot}
263 | The \func{4} function provides a way of visually viewing the structure of a comparison tree. It plots a directed graph. Note that the full names of each node are used because unique node names are required when plotting a directed graph. The plot for the example is shown in Figure~\ref{FIG:plot}.
264 |
265 | \begin{figure}[!htb]
266 | \centering
267 | \includegraphics[width=\textwidth]{../figs/Plot}
268 | \caption{Plot of the comparison tree.}
269 | \label{FIG:plot}
270 | \end{figure}
271 |
272 | \subsubsection{Printing a Summary of Changes}
273 | \label{SEC:summary}
274 | The \func{5} function prints a text summary of the changes to a file or to the command line. Feel free to make modifications and write your own queries to include in the report.
275 |
276 | \subsubsection{Highlight Nodes in the Model}
277 | \label{SEC:highlight}
278 | The \func{7} function highlights model elements corresponding to nodes. Optional arguments can be passed into the function to specify the colors and highlighting method. Please see the source code comment for more information. The highlighting can be done using the two available methods in \Simulink: using \href{https://www.mathworks.com/help/simulink/slref/hilite_system.html}{hilite\_system}, or by modifying the model element's \href{https://www.mathworks.com/help/simulink/ug/approach-modeling-programmatically.html#f4-93649}{ForegroundColor/BackgroundColor} parameters. The differences between the two approaches are elaborated on in Table~\ref{tbl:colorcomparison}.
279 |
280 | \begin{figure}[ht!]
281 | \begin{center}
282 | \begin{tabular}{p{16em} p{16em}}
283 | \hline
284 | Hilite\_system & Foreground/Background \\ \hline \hline
285 | Highlights SubSystem blocks when they are not modified themselves & Only highlights SubSystem blocks when they are listed in the nodes argument \\ \hline
286 | Disappears upon model close & Can be saved in the model \\ \hline
287 | Does not overwrite existing coloring & Overwrites previous coloring \textbf{Note:} To revert to previous highlighting, do not save, and then close and reopen the model \\ \hline
288 | To undo, right-click in the model and select \cmd{Remove Highlighting}. & Can remove \emph{all color} (i.e. change to default black/white), by running: \cmd{highlightNodes(nodes, sys, 'fg', 'black', 'bg', 'white')} \\ \hline
289 | Can do highlighting on a loaded model as well as an opened model & Can do highlighting on an opened model only \\ \hline
290 | \end{tabular}
291 | \end{center}
292 | \caption{The differences in highlighting methods.}
293 | \label{tbl:colorcomparison}
294 | \end{figure}
295 |
296 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297 | % Example
298 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
299 | \section{Example}
300 | \label{SEC:example}
301 | Two models are provided in the \file{example} folder for demonstration purposes. They are shown in Figure~\ref{FIG:demo}. These models have several differences as a result of the following changes:
302 |
303 | \begin{itemize}
304 | \item 3 deleted elements:
305 | \begin{itemize}
306 | \item \block{Integrator} block was deleted (and replaced by \block{Gain} block).
307 | \item The two lines going into/out of \block{Integrator} are implicitly considered deleted when \block{Integrator} is deleted, and then added when \block{Gain} is connected (therefore, they are also listed as added elements).
308 |
309 | \end{itemize}
310 | \item 4 added elements:
311 | \begin{itemize}
312 | \item \block{Data Store Memory} block was added.
313 | \item \block{Gain} block was added (in replacement of the \block{Integrator} block).
314 | \item The two lines going into/out of \block{Integrator} are implicitly considered deleted when \block{Integrator} is deleted, and then added when \block{Gain} is connected.
315 | \end{itemize}
316 |
317 | \item 2 modifications:
318 | \begin{itemize}
319 | \item \block{Add} block's \param{List of signs} property was changed from ++ to -- --.
320 | \item \block{Constant} block's \param{Constant value} property was changed from 1 to 2.
321 | \end{itemize}
322 | \item 1 renamed element:
323 | \begin{itemize}
324 | \item \block{Outport} block named \param{Out1} was renamed to \param{NewName}.
325 | \end{itemize}
326 | \end{itemize}
327 |
328 | \noindent
329 | To create the comparison tree, use the commands in the Command Window:
330 |
331 | \begin{lstlisting}
332 | model1 = 'demo_before';
333 | model2 = 'demo_after';
334 | load_system(model1);
335 | load_system(model2);
336 | Edits = slxmlcomp.compare(model1, model2)
337 | \end{lstlisting}
338 |
339 | After the \EditsObj object is created, the \ToolName can be used.
340 |
341 | \begin{figure}[!htb]
342 | \centering
343 | \begin{subfigure}[t]{.48\textwidth}
344 | \centering
345 | \includegraphics[width=\textwidth]{../figs/example/Before}
346 | \caption{Model Before Changes.}
347 | \end{subfigure}
348 | ~
349 | \begin{subfigure}[t]{.48\textwidth}
350 | \centering
351 | \includegraphics[width=\textwidth]{../figs/example/After}
352 | \caption{Model After Changes.}
353 | \end{subfigure}
354 | \caption{Two Versions of a single model.}
355 | \label{FIG:demo}
356 | \end{figure}
357 |
358 | \newpage
359 | \subsection{Finding Nodes}
360 | To find a specific set of nodes, use the \func{1} function. For example, to find only blocks that have been added, use the following command:
361 |
362 | \begin{lstlisting}
363 | added = find_node(Edits, 'ChangeType', 'added', 'NodeType', 'block')
364 |
365 | added =
366 |
367 | 2x1 Node array with properties:
368 |
369 | Children
370 | Edited
371 | Name
372 | Parameters
373 | Parent
374 | Partner
375 | \end{lstlisting}
376 |
377 | \subsection{Getting a Node's Handle in the Model}
378 | To determine the handle of one of the added nodes, use the following command. Note that because the node is added, that means that it only exists in the right sub-tree (see Table~\ref{TBL:Placement}), so we find the element in \texttt{model2}.
379 |
380 | \begin{lstlisting}
381 | h = getHandle(added(1), model2)
382 |
383 | h =
384 |
385 | 13.0004
386 | \end{lstlisting}
387 |
388 | \subsection{Getting a Node's Path in the Model}
389 | To determine the path of the added node in the model, use the following command. Again, because the node is added, that means that it only exists in the right sub-tree (see Table~\ref{TBL:Placement}), so we find the element in \texttt{model2}.
390 |
391 | \begin{lstlisting}
392 | p = getPath(added(1), model2)
393 |
394 | p =
395 |
396 | 'demo_after/Subsystem/Subsystem/Gain'
397 | \end{lstlisting}
398 |
399 | \subsection{Getting a Node's Path in the Tree}
400 | To determine the path of the node in the comparison tree, use the following command:
401 |
402 | \begin{lstlisting}
403 | pt = getPathTree(added(1))
404 |
405 | pt =
406 |
407 | 'Comparison Root/Simulink/Subsystem/Subsystem/Gain'
408 | \end{lstlisting}
409 |
410 | \subsection{Plotting the Comparison Tree}
411 | To visualize the comparison tree, use the following command to plot it. This will display Figure~\ref{FIG:plot}.
412 |
413 | \begin{lstlisting}
414 | plotTree(Edits);
415 | \end{lstlisting}
416 |
417 | \subsection{Printing a Summary of Changes}
418 | To print a textual report of the changes to the Command Window, use the following command. The format of the report is first the query information: $<$Query constraints$>$ -{}- TOTAL $<$\#$>$, following by the tree paths of the nodes. It is possible to print the summary to a \file{.txt} file, or omit including the paths. Please see the function's header comment for more information. Feel free to modify this report with the queries you require.
419 |
420 | \begin{lstlisting}
421 | >> summaryOfChanges(Edits, 1)
422 |
423 | ChangeType, added -- TOTAL 4
424 | Comparison Root/Simulink/Subsystem/Subsystem/Gain
425 | Comparison Root/Simulink/Subsystem/Subsystem/In2:1 -> Gain:1
426 | Comparison Root/Simulink/Subsystem/Subsystem/Gain:1 -> Out2:1
427 | Comparison Root/Simulink/Subsystem/Data Store Memory
428 | ChangeType, deleted -- TOTAL 3
429 | Comparison Root/Simulink/Subsystem/Subsystem/Integrator
430 | Comparison Root/Simulink/Subsystem/Subsystem/In2:1 -> Integrator:1
431 | Comparison Root/Simulink/Subsystem/Subsystem/Integrator:1 -> Out2:1
432 | ChangeType, renamed -- TOTAL 2
433 | Comparison Root/Simulink/Subsystem/Subsystem/Out1
434 | Comparison Root/Simulink/Subsystem/Subsystem/NewName
435 | ChangeType, modified -- TOTAL 4
436 | Comparison Root/Simulink/Subsystem/Subsystem/Add
437 | Comparison Root/Simulink/Subsystem/Subsystem/Constant
438 | Comparison Root/Simulink/Subsystem/Subsystem/Add
439 | Comparison Root/Simulink/Subsystem/Subsystem/Constant
440 |
441 | NodeType, block, ChangeType, added -- TOTAL 2
442 | Comparison Root/Simulink/Subsystem/Subsystem/Gain
443 | Comparison Root/Simulink/Subsystem/Data Store Memory
444 | NodeType, block, ChangeType, deleted -- TOTAL 1
445 | Comparison Root/Simulink/Subsystem/Subsystem/Integrator
446 | NodeType, block, ChangeType, renamed -- TOTAL 2
447 | Comparison Root/Simulink/Subsystem/Subsystem/Out1
448 | Comparison Root/Simulink/Subsystem/Subsystem/NewName
449 | NodeType, block, ChangeType, modified -- TOTAL 4
450 | Comparison Root/Simulink/Subsystem/Subsystem/Add
451 | Comparison Root/Simulink/Subsystem/Subsystem/Constant
452 | Comparison Root/Simulink/Subsystem/Subsystem/Add
453 | Comparison Root/Simulink/Subsystem/Subsystem/Constant
454 |
455 | NodeType, block, ChangeType, added, BlockType, inport -- TOTAL 0
456 | NodeType, block, ChangeType, deleted, BlockType, inport -- TOTAL 0
457 | NodeType, block, ChangeType, renamed, BlockType, inport -- TOTAL 0
458 | NodeType, block, ChangeType, modified, BlockType, inport -- TOTAL 0
459 |
460 | NodeType, block, ChangeType, added, BlockType, outport -- TOTAL 0
461 | NodeType, block, ChangeType, deleted, BlockType, outport -- TOTAL 0
462 | NodeType, block, ChangeType, renamed, BlockType, outport -- TOTAL 2
463 | Comparison Root/Simulink/Subsystem/Subsystem/Out1
464 | Comparison Root/Simulink/Subsystem/Subsystem/NewName
465 | NodeType, block, ChangeType, modified, BlockType, outport -- TOTAL 0
466 |
467 | NodeType, line, ChangeType, added -- TOTAL 2
468 | Comparison Root/Simulink/Subsystem/Subsystem/In2:1 -> Gain:1
469 | Comparison Root/Simulink/Subsystem/Subsystem/Gain:1 -> Out2:1
470 | NodeType, line, ChangeType, deleted -- TOTAL 2
471 | Comparison Root/Simulink/Subsystem/Subsystem/In2:1 -> Integrator:1
472 | Comparison Root/Simulink/Subsystem/Subsystem/Integrator:1 -> Out2:1
473 | NodeType, line, ChangeType, renamed -- TOTAL 0
474 | NodeType, line, ChangeType, modified -- TOTAL 0
475 | \end{lstlisting}
476 |
477 | \subsection{Highlighting Nodes in the Model}
478 | To highlight nodes in the model, use the following command. Note that you must specify which model to use, and that some nodes may not exist in that model (\eg deleted nodes, added nodes).
479 |
480 | \begin{lstlisting}
481 | >> open_system(mdl2);
482 | >> highlightNodes(added, mdl2);
483 | \end{lstlisting}
484 |
485 | \begin{thebibliography}{9}
486 | \bibitem{CompareXML}
487 | The MathWorks.
488 | ``Compare XML Files".
489 | \\\texttt{https://www.mathworks.com/help/matlab/matlab\_env/compare-xml-files.html}
490 | [Online; Accessed June 2020]
491 | \end{thebibliography}
492 |
493 | \end{document}
--------------------------------------------------------------------------------
/doc/tex/macros.tex:
--------------------------------------------------------------------------------
1 | \usepackage{xspace}
2 |
3 | \newcommand{\args}[1] {\textit{#1}}
4 | \newcommand{\cmd}[1] {\texttt{#1}} % Use for command window commands, e.g., \cmd{svn up}
5 | \newcommand{\block}[1] {\textsf{#1}} % Use for Simulink block names, e.g., \cmd{Subsystem1}
6 | \newcommand{\signal}[1] {\textsf{#1}} % Use for Simulink block names, e.g., \cmd{Subsystem1}
7 | \newcommand{\ring}[1] {\textsf{#1}} % Use for files names and paths
8 | \newcommand{\keyword}[1] {\texttt{#1}} % Use for keywords of programming languages, e.g., \keyword{while}
9 | \newcommand{\file}[1] {\texttt{#1}} % Use for files names and paths
10 | \newcommand{\param}[1] {\textsf{#1}} % Use for block parameter names, e.g., \param{BlockType}
11 |
12 | % Matlab Products
13 | \newcommand{\matlab}{\textsc{Matlab}\@\xspace}
14 | \newcommand{\Matlab}{\textsc{Matlab}\@\xspace}
15 | \newcommand{\Simulink}{Simulink\@\xspace}
16 | \newcommand{\simulink}{Simulink\@\xspace}
17 | \newcommand{\SDV}{Simulink Design Verifier\@\xspace}
18 | \newcommand{\mpath}{\Matlab search path\@\xspace}
19 |
20 | % Block Names (not BlockType)
21 | \newcommand{\ds}{\block{Data Store}\@\xspace}
22 | \newcommand{\DSM}{\block{Data Store Memory}\@\xspace}
23 | \newcommand{\DSR}{\block{Data Store Read}\@\xspace}
24 | \newcommand{\DSW}{\block{Data Store Write}\@\xspace}
25 | \newcommand{\DSRW}{\block{Data Store Read/Write}\@\xspace}
26 | \newcommand{\DSMRW}{\block{Data Store Memory/Read/Write}\@\xspace}
27 |
28 | \newcommand{\goto}{\block{Goto}\@\xspace}
29 | \newcommand{\from}{\block{From}\@\xspace}
30 |
31 | \newcommand{\inport}{\block{Inport}\@\xspace}
32 | \newcommand{\outport}{\block{Outport}\@\xspace}
33 | \newcommand{\constant}{\block{Constant}\@\xspace}
34 | \newcommand{\ground}{\block{Ground}\@\xspace}
35 | \newcommand{\subsystem}{\block{Subsystem}\@\xspace}
36 |
37 | \newcommand{\logic}{\block{Logical Operator}\@\xspace}
38 | \newcommand{\relational}{\block{Relational Operator}\@\xspace}
39 | \newcommand{\ifblk}{\block{If}\@\xspace}
40 | \newcommand{\switch}{\block{Switch}\@\xspace}
41 | \newcommand{\merge}{\block{Merge}\@\xspace}
42 |
43 | \newcommand{\docblock}{\block{DocBlock}\@\xspace}
44 |
45 | \newcommand{\simfunc}{\block{Simulink Function}\@\xspace}
46 | \newcommand{\simfunccaller}{\block{Function Caller}\@\xspace}
47 |
48 | \newcommand{\toworkspace}{\block{To Workspace}\@\xspace}
49 | \newcommand{\fromworkspace}{\block{From Workspace}\@\xspace}
50 |
51 | \newcommand{\tofile}{\block{To File}\@\xspace}
52 | \newcommand{\fromfile}{\block{From File}\@\xspace}
53 |
54 | \newcommand{\fromspreadsheet}{\block{From Spreadsheet}\@\xspace}
55 |
56 | \newcommand{\modelref}{\block{Model Reference}\@\xspace}
57 | \newcommand{\library}{\block{Library}\@\xspace}
58 | \newcommand{\librarylink}{\block{Library Link}\@\xspace}
59 |
60 | % Commonly used parameters
61 | \newcommand{\AND}{\param{AND}\@\xspace}
62 | \newcommand{\OR}{\param{OR}\@\xspace}
63 | \newcommand{\NOT}{\param{NOT}\@\xspace}
64 | \newcommand{\NOR}{\param{NOR}\@\xspace}
65 | \newcommand{\NAND}{\param{NAND}\@\xspace}
66 | \newcommand{\XOR}{\param{XOR}\@\xspace}
67 | \newcommand{\NXOR}{\param{NXOR}\@\xspace}
68 |
69 | % Common Abbreviations
70 | % Example
71 | \newcommand{\eg}{\textrm{e.g.,}\@\xspace}
72 |
73 | % That Is To Say
74 | \newcommand{\ie}{\textrm{i.e.,}\@\xspace}
75 |
76 | % And So On
77 | \newcommand{\etc}{\textrm{etc.}\@\xspace}
78 |
79 | % And Others
80 | \newcommand{\etal}{\textrm{et al.}\@\xspace}
81 |
82 | % With Respect To
83 | \newcommand{\wrt}{\textrm{w.r.t.}\@\xspace}
84 |
85 | % Vice Versa
86 | \newcommand{\vrsa}{\textrm{vice versa}\@\xspace}
87 |
88 | % Symbols
89 |
90 | \usepackage{amssymb}
91 | \newcommand{\checkbox}{\makebox[0pt][l]{$\square$}\raisebox{.15ex}{\hspace{0.1em}$\checkmark$}}%
92 | \newcommand{\uncheckbox}{$\square$~}%
93 |
--------------------------------------------------------------------------------
/example/demo_before.mdl:
--------------------------------------------------------------------------------
1 | Model {
2 | Name "demo_before"
3 | Version 8.8
4 | SavedCharacterEncoding "windows-1252"
5 | GraphicalInterface {
6 | NumRootInports 2
7 | Inport {
8 | Name "In1"
9 | BusObject ""
10 | OutputFunctionCall "off"
11 | UnitExpr "inherit"
12 | }
13 | Inport {
14 | Name "In2"
15 | BusObject ""
16 | OutputFunctionCall "off"
17 | UnitExpr "inherit"
18 | }
19 | NumRootOutports 2
20 | Outport {
21 | Name "Out1"
22 | BusObject ""
23 | BusOutputAsStruct "off"
24 | UnitExpr "inherit"
25 | }
26 | Outport {
27 | Name "Out2"
28 | BusObject ""
29 | BusOutputAsStruct "off"
30 | UnitExpr "inherit"
31 | }
32 | ParameterArgumentNames ""
33 | ComputedModelVersion "1.12"
34 | NumModelReferences 0
35 | NumTestPointedSignals 0
36 | NumProvidedFunctions 0
37 | NumRequiredFunctions 0
38 | NumResetEvents 0
39 | HasInitializeEvent 0
40 | HasTerminateEvent 0
41 | IsExportFunctionModel 0
42 | }
43 | LogicAnalyzerGraphicalSettings ""
44 | LogicAnalyzerPlugin "on"
45 | LogicAnalyzerSignalOrdering ""
46 | DiagnosticSuppressor "on"
47 | SuppressorTable "22 serialization::archive 11 0 3 0 0 0 8 0"
48 | ScopeRefreshTime 0.035000
49 | OverrideScopeRefreshTime on
50 | DisableAllScopes off
51 | DataTypeOverride "UseLocalSettings"
52 | DataTypeOverrideAppliesTo "AllNumericTypes"
53 | MinMaxOverflowLogging "UseLocalSettings"
54 | MinMaxOverflowArchiveMode "Overwrite"
55 | FPTRunName "Run 1"
56 | MaxMDLFileLineLength 120
57 | LastSavedArchitecture "win64"
58 | Object {
59 | $PropName "BdWindowsInfo"
60 | $ObjectID 1
61 | $ClassName "Simulink.BDWindowsInfo"
62 | Object {
63 | $PropName "WindowsInfo"
64 | $ObjectID 2
65 | $ClassName "Simulink.WindowInfo"
66 | IsActive [1]
67 | Location [53.0, 52.0, 872.0, 750.0]
68 | Object {
69 | $PropName "ModelBrowserInfo"
70 | $ObjectID 3
71 | $ClassName "Simulink.ModelBrowserInfo"
72 | Visible [0]
73 | DockPosition "Left"
74 | Width [50]
75 | Height [50]
76 | Filter [8]
77 | }
78 | Object {
79 | $PropName "ExplorerBarInfo"
80 | $ObjectID 4
81 | $ClassName "Simulink.ExplorerBarInfo"
82 | Visible [1]
83 | }
84 | Object {
85 | $PropName "EditorsInfo"
86 | $ObjectID 5
87 | $ClassName "Simulink.EditorInfo"
88 | IsActive [1]
89 | ViewObjType "SimulinkTopLevel"
90 | LoadSaveID "0"
91 | Extents [822.0, 571.0]
92 | ZoomFactor [1.0]
93 | Offset [0.0, 0.0]
94 | }
95 | Object {
96 | $PropName "DockComponentsInfo"
97 | $ObjectID 6
98 | $ClassName "Simulink.DockComponentInfo"
99 | Type "GLUE2:PropertyInspector"
100 | ID "Property Inspector"
101 | Visible [0]
102 | CreateCallback ""
103 | UserData ""
104 | Floating [0]
105 | DockPosition "Right"
106 | Width [640]
107 | Height [480]
108 | }
109 | WindowState "AAAA/wAAAAD9AAAAAgAAAAAAAAC9AAAB+PwCAAAAA/sAAAAWAEQAbwBjAGsAVwBpAGQAZwBlAHQAMwEAAAAxAAAB+AAAA"
110 | "AAAAAAA+wAAABYARABvAGMAawBXAGkAZABnAGUAdAA0AAAAAAD/////AAAAAAAAAAD7AAAAUgBHAEwAVQBFADIAIAB0AHIAZQBlACAAYwBvAG0Ac"
111 | "ABvAG4AZQBuAHQALwBHAEwAVQBFADIAIAB0AHIAZQBlACAAYwBvAG0AcABvAG4AZQBuAHQAAAAAAP////8AAABfAP///wAAAAEAAAAAAAAAAPwCA"
112 | "AAAAfsAAABUAEcATABVAEUAMgA6AFAAcgBvAHAAZQByAHQAeQBJAG4AcwBwAGUAYwB0AG8AcgAvAFAAcgBvAHAAZQByAHQAeQAgAEkAbgBzAHAAZ"
113 | "QBjAHQAbwByAAAAAAD/////AAAAJwD///8AAANYAAACcQAAAAEAAAACAAAAAQAAAAL8AAAAAQAAAAIAAAAP/////wAAAAAA/////wAAAAAAAAAA/"
114 | "////wEAAAAA/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/"
115 | "////wEAAAB5/////wAAAAAAAAAA/////wEAAADa/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/////wEAAAFT/////wAAAAAAAAAA/"
116 | "////wAAAAAA/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA/////wEAAAL6/////wAAAAAAAAAA/"
117 | "////wEAAAMp/////wAAAAAAAAAA/////wAAAAAA/////wAAAAAAAAAA"
118 | }
119 | }
120 | Created "Sun Jan 07 11:26:29 2018"
121 | Creator "monika"
122 | UpdateHistory "UpdateHistoryNever"
123 | ModifiedByFormat "%"
124 | LastModifiedBy "monika"
125 | ModifiedDateFormat "%"
126 | LastModifiedDate "Thu Jun 13 10:05:33 2019"
127 | RTWModifiedTimeStamp 482321125
128 | ModelVersionFormat "1.%"
129 | ConfigurationManager "none"
130 | SampleTimeColors off
131 | SampleTimeAnnotations off
132 | LibraryLinkDisplay "disabled"
133 | WideLines off
134 | ShowLineDimensions off
135 | ShowPortDataTypes off
136 | ShowEditTimeErrors on
137 | ShowEditTimeWarnings on
138 | ShowEditTimeAdvisorChecks off
139 | ShowPortUnits off
140 | ShowDesignRanges off
141 | ShowLoopsOnError on
142 | IgnoreBidirectionalLines off
143 | ShowStorageClass off
144 | ShowTestPointIcons on
145 | ShowSignalResolutionIcons on
146 | ShowViewerIcons on
147 | SortedOrder off
148 | VariantCondition off
149 | ExecutionContextIcon off
150 | ShowLinearizationAnnotations on
151 | ShowVisualizeInsertedRTB on
152 | ShowMarkup on
153 | BlockNameDataTip off
154 | BlockParametersDataTip off
155 | BlockDescriptionStringDataTip off
156 | ToolBar on
157 | StatusBar on
158 | BrowserShowLibraryLinks off
159 | FunctionConnectors off
160 | BrowserLookUnderMasks off
161 | SimulationMode "normal"
162 | PauseTimes "5"
163 | NumberOfSteps 1
164 | SnapshotBufferSize 10
165 | SnapshotInterval 10
166 | NumberOfLastSnapshots 0
167 | LinearizationMsg "none"
168 | Profile off
169 | ParamWorkspaceSource "MATLABWorkspace"
170 | AccelSystemTargetFile "accel.tlc"
171 | AccelTemplateMakefile "accel_default_tmf"
172 | AccelMakeCommand "make_rtw"
173 | TryForcingSFcnDF off
174 | Object {
175 | $PropName "DataLoggingOverride"
176 | $ObjectID 7
177 | $ClassName "Simulink.SimulationData.ModelLoggingInfo"
178 | model_ "demo_before"
179 | overrideMode_ [0.0]
180 | Array {
181 | Type "Cell"
182 | Dimension 1
183 | Cell "demo_before"
184 | PropName "logAsSpecifiedByModels_"
185 | }
186 | Array {
187 | Type "Cell"
188 | Dimension 1
189 | Cell []
190 | PropName "logAsSpecifiedByModelsSSIDs_"
191 | }
192 | }
193 | ExtModeBatchMode off
194 | ExtModeEnableFloating on
195 | ExtModeTrigType "manual"
196 | ExtModeTrigMode "normal"
197 | ExtModeTrigPort "1"
198 | ExtModeTrigElement "any"
199 | ExtModeTrigDuration 1000
200 | ExtModeTrigDurationFloating "auto"
201 | ExtModeTrigHoldOff 0
202 | ExtModeTrigDelay 0
203 | ExtModeTrigDirection "rising"
204 | ExtModeTrigLevel 0
205 | ExtModeArchiveMode "off"
206 | ExtModeAutoIncOneShot off
207 | ExtModeIncDirWhenArm off
208 | ExtModeAddSuffixToVar off
209 | ExtModeWriteAllDataToWs off
210 | ExtModeArmWhenConnect on
211 | ExtModeSkipDownloadWhenConnect off
212 | ExtModeLogAll on
213 | ExtModeAutoUpdateStatusClock on
214 | ShowModelReferenceBlockVersion off
215 | ShowModelReferenceBlockIO off
216 | Array {
217 | Type "Handle"
218 | Dimension 1
219 | Simulink.ConfigSet {
220 | $ObjectID 8
221 | Version "1.16.5"
222 | DisabledProps []
223 | Description ""
224 | Array {
225 | Type "Handle"
226 | Dimension 9
227 | Simulink.SolverCC {
228 | $ObjectID 9
229 | Version "1.16.5"
230 | DisabledProps []
231 | Description ""
232 | StartTime "0.0"
233 | StopTime "10.0"
234 | AbsTol "auto"
235 | FixedStep "auto"
236 | InitialStep "auto"
237 | MaxNumMinSteps "-1"
238 | MaxOrder 5
239 | ZcThreshold "auto"
240 | ConsecutiveZCsStepRelTol "10*128*eps"
241 | MaxConsecutiveZCs "1000"
242 | ExtrapolationOrder 4
243 | NumberNewtonIterations 1
244 | MaxStep "auto"
245 | MinStep "auto"
246 | MaxConsecutiveMinStep "1"
247 | RelTol "1e-3"
248 | EnableMultiTasking off
249 | EnableConcurrentExecution off
250 | ConcurrentTasks off
251 | Solver "ode45"
252 | SolverName "ode45"
253 | SolverJacobianMethodControl "auto"
254 | ShapePreserveControl "DisableAll"
255 | ZeroCrossControl "UseLocalSettings"
256 | ZeroCrossAlgorithm "Nonadaptive"
257 | AlgebraicLoopSolver "TrustRegion"
258 | SolverInfoToggleStatus off
259 | IsAutoAppliedInSIP off
260 | SolverResetMethod "Fast"
261 | PositivePriorityOrder off
262 | AutoInsertRateTranBlk off
263 | SampleTimeConstraint "Unconstrained"
264 | InsertRTBMode "Whenever possible"
265 | SampleTimeProperty []
266 | }
267 | Simulink.DataIOCC {
268 | $ObjectID 10
269 | Version "1.16.5"
270 | DisabledProps []
271 | Description ""
272 | Decimation "1"
273 | ExternalInput "[t, u]"
274 | FinalStateName "xFinal"
275 | InitialState "xInitial"
276 | LimitDataPoints off
277 | MaxDataPoints "1000"
278 | LoadExternalInput off
279 | LoadInitialState off
280 | SaveFinalState off
281 | SaveCompleteFinalSimState off
282 | SaveFormat "StructureWithTime"
283 | SignalLoggingSaveFormat "Dataset"
284 | SaveOutput on
285 | SaveState off
286 | SignalLogging on
287 | DSMLogging on
288 | InspectSignalLogs off
289 | VisualizeSimOutput on
290 | StreamToWorkspace off
291 | StreamVariableName "streamout"
292 | SaveTime on
293 | ReturnWorkspaceOutputs off
294 | StateSaveName "xout"
295 | TimeSaveName "tout"
296 | OutputSaveName "yout"
297 | SignalLoggingName "logsout"
298 | DSMLoggingName "dsmout"
299 | OutputOption "RefineOutputTimes"
300 | OutputTimes "[]"
301 | ReturnWorkspaceOutputsName "out"
302 | Refine "1"
303 | LoggingToFile off
304 | LoggingFileName "out.mat"
305 | LoggingIntervals "[-inf, inf]"
306 | }
307 | Simulink.OptimizationCC {
308 | $ObjectID 11
309 | Version "1.16.5"
310 | Array {
311 | Type "Cell"
312 | Dimension 8
313 | Cell "BooleansAsBitfields"
314 | Cell "PassReuseOutputArgsAs"
315 | Cell "PassReuseOutputArgsThreshold"
316 | Cell "ZeroExternalMemoryAtStartup"
317 | Cell "ZeroInternalMemoryAtStartup"
318 | Cell "OptimizeModelRefInitCode"
319 | Cell "NoFixptDivByZeroProtection"
320 | Cell "UseSpecifiedMinMax"
321 | PropName "DisabledProps"
322 | }
323 | Description ""
324 | BlockReduction on
325 | BooleanDataType on
326 | ConditionallyExecuteInputs on
327 | DefaultParameterBehavior "Tunable"
328 | UseDivisionForNetSlopeComputation "off"
329 | UseFloatMulNetSlope off
330 | DefaultUnderspecifiedDataType "double"
331 | UseSpecifiedMinMax off
332 | InlineInvariantSignals off
333 | OptimizeBlockIOStorage on
334 | BufferReuse on
335 | EnhancedBackFolding off
336 | CachingGlobalReferences off
337 | GlobalBufferReuse on
338 | StrengthReduction off
339 | AdvancedOptControl ""
340 | ExpressionFolding on
341 | BooleansAsBitfields off
342 | BitfieldContainerType "uint_T"
343 | EnableMemcpy on
344 | MemcpyThreshold 64
345 | PassReuseOutputArgsAs "Structure reference"
346 | PassReuseOutputArgsThreshold 12
347 | ExpressionDepthLimit 128
348 | LocalBlockOutputs on
349 | RollThreshold 5
350 | StateBitsets off
351 | DataBitsets off
352 | ActiveStateOutputEnumStorageType "Native Integer"
353 | ZeroExternalMemoryAtStartup on
354 | ZeroInternalMemoryAtStartup on
355 | InitFltsAndDblsToZero off
356 | NoFixptDivByZeroProtection off
357 | EfficientFloat2IntCast off
358 | EfficientMapNaN2IntZero on
359 | LifeSpan "inf"
360 | MaxStackSize "Inherit from target"
361 | BufferReusableBoundary on
362 | SimCompilerOptimization "off"
363 | AccelVerboseBuild off
364 | }
365 | Simulink.DebuggingCC {
366 | $ObjectID 12
367 | Version "1.16.5"
368 | Array {
369 | Type "Cell"
370 | Dimension 1
371 | Cell "UseOnlyExistingSharedCode"
372 | PropName "DisabledProps"
373 | }
374 | Description ""
375 | RTPrefix "error"
376 | ConsistencyChecking "none"
377 | ArrayBoundsChecking "none"
378 | SignalInfNanChecking "none"
379 | SignalRangeChecking "none"
380 | ReadBeforeWriteMsg "UseLocalSettings"
381 | WriteAfterWriteMsg "UseLocalSettings"
382 | WriteAfterReadMsg "UseLocalSettings"
383 | AlgebraicLoopMsg "warning"
384 | ArtificialAlgebraicLoopMsg "warning"
385 | SaveWithDisabledLinksMsg "warning"
386 | SaveWithParameterizedLinksMsg "warning"
387 | CheckSSInitialOutputMsg on
388 | UnderspecifiedInitializationDetection "Simplified"
389 | MergeDetectMultiDrivingBlocksExec "error"
390 | CheckExecutionContextPreStartOutputMsg off
391 | CheckExecutionContextRuntimeOutputMsg off
392 | SignalResolutionControl "UseLocalSettings"
393 | BlockPriorityViolationMsg "warning"
394 | MinStepSizeMsg "warning"
395 | TimeAdjustmentMsg "none"
396 | MaxConsecutiveZCsMsg "error"
397 | MaskedZcDiagnostic "warning"
398 | IgnoredZcDiagnostic "warning"
399 | SolverPrmCheckMsg "none"
400 | InheritedTsInSrcMsg "warning"
401 | MultiTaskDSMMsg "error"
402 | MultiTaskCondExecSysMsg "error"
403 | MultiTaskRateTransMsg "error"
404 | SingleTaskRateTransMsg "none"
405 | TasksWithSamePriorityMsg "warning"
406 | SigSpecEnsureSampleTimeMsg "warning"
407 | CheckMatrixSingularityMsg "none"
408 | IntegerOverflowMsg "warning"
409 | Int32ToFloatConvMsg "warning"
410 | ParameterDowncastMsg "error"
411 | ParameterOverflowMsg "error"
412 | ParameterUnderflowMsg "none"
413 | ParameterPrecisionLossMsg "warning"
414 | ParameterTunabilityLossMsg "warning"
415 | FixptConstUnderflowMsg "none"
416 | FixptConstOverflowMsg "none"
417 | FixptConstPrecisionLossMsg "none"
418 | UnderSpecifiedDataTypeMsg "none"
419 | UnnecessaryDatatypeConvMsg "none"
420 | VectorMatrixConversionMsg "none"
421 | InvalidFcnCallConnMsg "error"
422 | FcnCallInpInsideContextMsg "warning"
423 | SignalLabelMismatchMsg "none"
424 | UnconnectedInputMsg "warning"
425 | UnconnectedOutputMsg "warning"
426 | UnconnectedLineMsg "warning"
427 | UseOnlyExistingSharedCode "error"
428 | SFcnCompatibilityMsg "none"
429 | FrameProcessingCompatibilityMsg "error"
430 | UniqueDataStoreMsg "none"
431 | BusObjectLabelMismatch "warning"
432 | RootOutportRequireBusObject "warning"
433 | AssertControl "UseLocalSettings"
434 | AllowSymbolicDim on
435 | RowMajorDimensionSupport off
436 | ModelReferenceIOMsg "none"
437 | ModelReferenceMultiInstanceNormalModeStructChecksumCheck "error"
438 | ModelReferenceVersionMismatchMessage "none"
439 | ModelReferenceIOMismatchMessage "none"
440 | UnknownTsInhSupMsg "warning"
441 | ModelReferenceDataLoggingMessage "warning"
442 | ModelReferenceSymbolNameMessage "warning"
443 | ModelReferenceExtraNoncontSigs "error"
444 | StateNameClashWarn "none"
445 | SimStateInterfaceChecksumMismatchMsg "warning"
446 | SimStateOlderReleaseMsg "error"
447 | InitInArrayFormatMsg "warning"
448 | StrictBusMsg "ErrorLevel1"
449 | BusNameAdapt "WarnAndRepair"
450 | NonBusSignalsTreatedAsBus "none"
451 | SymbolicDimMinMaxWarning "warning"
452 | LossOfSymbolicDimsSimulationWarning "warning"
453 | LossOfSymbolicDimsCodeGenerationWarning "error"
454 | BlockIODiagnostic "none"
455 | SFUnusedDataAndEventsDiag "warning"
456 | SFUnexpectedBacktrackingDiag "error"
457 | SFInvalidInputDataAccessInChartInitDiag "warning"
458 | SFNoUnconditionalDefaultTransitionDiag "error"
459 | SFTransitionOutsideNaturalParentDiag "warning"
460 | SFUnreachableExecutionPathDiag "warning"
461 | SFUndirectedBroadcastEventsDiag "warning"
462 | SFTransitionActionBeforeConditionDiag "warning"
463 | SFOutputUsedAsStateInMooreChartDiag "error"
464 | SFTemporalDelaySmallerThanSampleTimeDiag "warning"
465 | SFSelfTransitionDiag "warning"
466 | SFExecutionAtInitializationDiag "none"
467 | SFMachineParentedDataDiag "warning"
468 | IntegerSaturationMsg "warning"
469 | AllowedUnitSystems "all"
470 | UnitsInconsistencyMsg "warning"
471 | AllowAutomaticUnitConversions on
472 | }
473 | Simulink.HardwareCC {
474 | $ObjectID 13
475 | Version "1.16.5"
476 | DisabledProps []
477 | Description ""
478 | ProdBitPerChar 8
479 | ProdBitPerShort 16
480 | ProdBitPerInt 32
481 | ProdBitPerLong 32
482 | ProdBitPerLongLong 64
483 | ProdBitPerFloat 32
484 | ProdBitPerDouble 64
485 | ProdBitPerPointer 64
486 | ProdBitPerSizeT 32
487 | ProdBitPerPtrDiffT 32
488 | ProdLargestAtomicInteger "Char"
489 | ProdLargestAtomicFloat "Float"
490 | ProdIntDivRoundTo "Zero"
491 | ProdEndianess "LittleEndian"
492 | ProdWordSize 32
493 | ProdShiftRightIntArith on
494 | ProdLongLongMode off
495 | ProdHWDeviceType "Specified"
496 | TargetBitPerChar 8
497 | TargetBitPerShort 16
498 | TargetBitPerInt 32
499 | TargetBitPerLong 32
500 | TargetBitPerLongLong 64
501 | TargetBitPerFloat 32
502 | TargetBitPerDouble 64
503 | TargetBitPerPointer 32
504 | TargetBitPerSizeT 32
505 | TargetBitPerPtrDiffT 32
506 | TargetLargestAtomicInteger "Char"
507 | TargetLargestAtomicFloat "None"
508 | TargetShiftRightIntArith on
509 | TargetLongLongMode off
510 | TargetIntDivRoundTo "Undefined"
511 | TargetEndianess "Unspecified"
512 | TargetWordSize 32
513 | TargetPreprocMaxBitsSint 32
514 | TargetPreprocMaxBitsUint 32
515 | TargetHWDeviceType "Specified"
516 | TargetUnknown off
517 | ProdEqTarget on
518 | UseEmbeddedCoderFeatures on
519 | UseSimulinkCoderFeatures on
520 | }
521 | Simulink.ModelReferenceCC {
522 | $ObjectID 14
523 | Version "1.16.5"
524 | DisabledProps []
525 | Description ""
526 | UpdateModelReferenceTargets "IfOutOfDateOrStructuralChange"
527 | EnableRefExpFcnMdlSchedulingChecks on
528 | CheckModelReferenceTargetMessage "error"
529 | EnableParallelModelReferenceBuilds off
530 | ParallelModelReferenceErrorOnInvalidPool on
531 | ParallelModelReferenceMATLABWorkerInit "None"
532 | ModelReferenceNumInstancesAllowed "Multi"
533 | PropagateVarSize "Infer from blocks in model"
534 | ModelDependencies ""
535 | ModelReferencePassRootInputsByReference on
536 | ModelReferenceMinAlgLoopOccurrences off
537 | PropagateSignalLabelsOutOfModel off
538 | SupportModelReferenceSimTargetCustomCode off
539 | }
540 | Simulink.SFSimCC {
541 | $ObjectID 15
542 | Version "1.16.5"
543 | DisabledProps []
544 | Description ""
545 | SimCustomSourceCode ""
546 | SimCustomHeaderCode ""
547 | SimCustomInitializer ""
548 | SimCustomTerminator ""
549 | SimReservedNameArray []
550 | SimUserSources ""
551 | SimUserIncludeDirs ""
552 | SimUserLibraries ""
553 | SimUserDefines ""
554 | SimCustomCompilerFlags ""
555 | SimCustomLinkerFlags ""
556 | SFSimEcho on
557 | SimCtrlC on
558 | SimIntegrity on
559 | SimUseLocalCustomCode off
560 | SimParseCustomCode on
561 | SimBuildMode "sf_incremental_build"
562 | SimGenImportedTypeDefs off
563 | ModelFunctionsGlobalVisibility "on"
564 | CompileTimeRecursionLimit 50
565 | EnableRuntimeRecursion on
566 | }
567 | Simulink.RTWCC {
568 | $BackupClass "Simulink.RTWCC"
569 | $ObjectID 16
570 | Version "1.16.5"
571 | Array {
572 | Type "Cell"
573 | Dimension 16
574 | Cell "IncludeHyperlinkInReport"
575 | Cell "GenerateTraceInfo"
576 | Cell "GenerateTraceReport"
577 | Cell "GenerateTraceReportSl"
578 | Cell "GenerateTraceReportSf"
579 | Cell "GenerateTraceReportEml"
580 | Cell "PortableWordSizes"
581 | Cell "GenerateWebview"
582 | Cell "GenerateCodeMetricsReport"
583 | Cell "GenerateCodeReplacementReport"
584 | Cell "GenerateMissedCodeReplacementReport"
585 | Cell "GenerateErtSFunction"
586 | Cell "CreateSILPILBlock"
587 | Cell "CodeExecutionProfiling"
588 | Cell "CodeProfilingSaveOptions"
589 | Cell "CodeProfilingInstrumentation"
590 | PropName "DisabledProps"
591 | }
592 | SystemTargetFile "grt.tlc"
593 | HardwareBoard "None"
594 | TLCOptions ""
595 | GenCodeOnly off
596 | MakeCommand "make_rtw"
597 | GenerateMakefile on
598 | PackageGeneratedCodeAndArtifacts off
599 | PackageName ""
600 | TemplateMakefile "grt_default_tmf"
601 | PostCodeGenCommand ""
602 | Description ""
603 | GenerateReport off
604 | SaveLog off
605 | RTWVerbose on
606 | RetainRTWFile off
607 | RTWBuildHooks []
608 | ProfileTLC off
609 | TLCDebug off
610 | TLCCoverage off
611 | TLCAssert off
612 | RTWUseLocalCustomCode off
613 | RTWUseSimCustomCode off
614 | CustomSourceCode ""
615 | CustomHeaderCode ""
616 | CustomInclude ""
617 | CustomSource ""
618 | CustomLibrary ""
619 | CustomDefine ""
620 | CustomLAPACKCallback ""
621 | CustomInitializer ""
622 | CustomTerminator ""
623 | Toolchain "Automatically locate an installed toolchain"
624 | BuildConfiguration "Faster Builds"
625 | CustomToolchainOptions []
626 | IncludeHyperlinkInReport off
627 | LaunchReport off
628 | PortableWordSizes off
629 | CreateSILPILBlock "None"
630 | CodeExecutionProfiling off
631 | CodeExecutionProfileVariable "executionProfile"
632 | CodeProfilingSaveOptions "SummaryOnly"
633 | CodeProfilingInstrumentation off
634 | SILDebugging off
635 | TargetLang "C"
636 | IncludeBusHierarchyInRTWFileBlockHierarchyMap off
637 | GenerateTraceInfo off
638 | GenerateTraceReport off
639 | GenerateTraceReportSl off
640 | GenerateTraceReportSf off
641 | GenerateTraceReportEml off
642 | GenerateWebview off
643 | GenerateCodeMetricsReport off
644 | GenerateCodeReplacementReport off
645 | GenerateMissedCodeReplacementReport off
646 | RTWCompilerOptimization "off"
647 | ObjectivePriorities []
648 | RTWCustomCompilerOptimizations ""
649 | CheckMdlBeforeBuild "Off"
650 | SharedConstantsCachingThreshold 1024
651 | Array {
652 | Type "Handle"
653 | Dimension 2
654 | Simulink.CodeAppCC {
655 | $ObjectID 17
656 | Version "1.16.5"
657 | Array {
658 | Type "Cell"
659 | Dimension 28
660 | Cell "IgnoreCustomStorageClasses"
661 | Cell "IgnoreTestpoints"
662 | Cell "InsertBlockDesc"
663 | Cell "InsertPolySpaceComments"
664 | Cell "SFDataObjDesc"
665 | Cell "MATLABFcnDesc"
666 | Cell "SimulinkDataObjDesc"
667 | Cell "DefineNamingRule"
668 | Cell "SignalNamingRule"
669 | Cell "ParamNamingRule"
670 | Cell "InternalIdentifier"
671 | Cell "InlinedPrmAccess"
672 | Cell "CustomSymbolStr"
673 | Cell "CustomSymbolStrGlobalVar"
674 | Cell "CustomSymbolStrType"
675 | Cell "CustomSymbolStrField"
676 | Cell "CustomSymbolStrFcn"
677 | Cell "CustomSymbolStrModelFcn"
678 | Cell "CustomSymbolStrFcnArg"
679 | Cell "CustomSymbolStrBlkIO"
680 | Cell "CustomSymbolStrTmpVar"
681 | Cell "CustomSymbolStrMacro"
682 | Cell "CustomSymbolStrUtil"
683 | Cell "CustomSymbolStrEmxType"
684 | Cell "CustomSymbolStrEmxFcn"
685 | Cell "CustomUserTokenString"
686 | Cell "ReqsInCode"
687 | Cell "BlockCommentType"
688 | PropName "DisabledProps"
689 | }
690 | Description ""
691 | Comment ""
692 | ForceParamTrailComments off
693 | GenerateComments on
694 | CommentStyle "Auto"
695 | IgnoreCustomStorageClasses on
696 | IgnoreTestpoints off
697 | IncHierarchyInIds off
698 | MaxIdLength 31
699 | PreserveName off
700 | PreserveNameWithParent off
701 | ShowEliminatedStatement off
702 | OperatorAnnotations off
703 | IncAutoGenComments off
704 | SimulinkDataObjDesc off
705 | SFDataObjDesc off
706 | MATLABFcnDesc off
707 | IncDataTypeInIds off
708 | MangleLength 1
709 | CustomSymbolStrGlobalVar "$R$N$M"
710 | CustomSymbolStrType "$N$R$M_T"
711 | CustomSymbolStrField "$N$M"
712 | CustomSymbolStrFcn "$R$N$M$F"
713 | CustomSymbolStrModelFcn "$R$N"
714 | CustomSymbolStrFcnArg "rt$I$N$M"
715 | CustomSymbolStrBlkIO "rtb_$N$M"
716 | CustomSymbolStrTmpVar "$N$M"
717 | CustomSymbolStrMacro "$R$N$M"
718 | CustomSymbolStrUtil "$N$C"
719 | CustomUserTokenString ""
720 | CustomCommentsFcn ""
721 | DefineNamingRule "None"
722 | DefineNamingFcn ""
723 | ParamNamingRule "None"
724 | ParamNamingFcn ""
725 | SignalNamingRule "None"
726 | SignalNamingFcn ""
727 | InsertBlockDesc off
728 | InsertPolySpaceComments off
729 | SimulinkBlockComments on
730 | MATLABSourceComments off
731 | EnableCustomComments off
732 | InternalIdentifierFile ""
733 | InternalIdentifier "Shortened"
734 | InlinedPrmAccess "Literals"
735 | ReqsInCode off
736 | UseSimReservedNames off
737 | ReservedNameArray []
738 | }
739 | Simulink.GRTTargetCC {
740 | $BackupClass "Simulink.TargetCC"
741 | $ObjectID 18
742 | Version "1.16.5"
743 | Array {
744 | Type "Cell"
745 | Dimension 15
746 | Cell "IncludeMdlTerminateFcn"
747 | Cell "SuppressErrorStatus"
748 | Cell "ERTCustomFileBanners"
749 | Cell "GenerateSampleERTMain"
750 | Cell "ExistingSharedCode"
751 | Cell "GenerateTestInterfaces"
752 | Cell "ModelStepFunctionPrototypeControlCompliant"
753 | Cell "GenerateAllocFcn"
754 | Cell "PurelyIntegerCode"
755 | Cell "SupportComplex"
756 | Cell "SupportAbsoluteTime"
757 | Cell "SupportContinuousTime"
758 | Cell "SupportNonInlinedSFcns"
759 | Cell "RemoveDisableFunc"
760 | Cell "RemoveResetFunc"
761 | PropName "DisabledProps"
762 | }
763 | Description ""
764 | TargetFcnLib "ansi_tfl_table_tmw.mat"
765 | TargetLibSuffix ""
766 | TargetPreCompLibLocation ""
767 | GenFloatMathFcnCalls "NOT IN USE"
768 | TargetLangStandard "C99 (ISO)"
769 | CodeReplacementLibrary "None"
770 | UtilityFuncGeneration "Auto"
771 | ERTMultiwordTypeDef "System defined"
772 | ERTMultiwordLength 256
773 | MultiwordLength 2048
774 | GenerateFullHeader on
775 | InferredTypesCompatibility off
776 | ExistingSharedCode ""
777 | GenerateSampleERTMain off
778 | GenerateTestInterfaces off
779 | ModelReferenceCompliant on
780 | ParMdlRefBuildCompliant on
781 | CompOptLevelCompliant on
782 | ConcurrentExecutionCompliant on
783 | IncludeMdlTerminateFcn on
784 | GeneratePreprocessorConditionals "Use local settings"
785 | CombineOutputUpdateFcns off
786 | CombineSignalStateStructs off
787 | SuppressErrorStatus off
788 | ERTFirstTimeCompliant off
789 | IncludeFileDelimiter "Auto"
790 | ERTCustomFileBanners off
791 | SupportAbsoluteTime on
792 | LogVarNameModifier "rt_"
793 | MatFileLogging on
794 | MultiInstanceERTCode off
795 | CodeInterfacePackaging "Nonreusable function"
796 | SupportNonFinite on
797 | SupportComplex on
798 | PurelyIntegerCode off
799 | SupportContinuousTime on
800 | SupportNonInlinedSFcns on
801 | RemoveDisableFunc off
802 | RemoveResetFunc off
803 | SupportVariableSizeSignals off
804 | ParenthesesLevel "Nominal"
805 | CastingMode "Nominal"
806 | MATLABClassNameForMDSCustomization "Simulink.SoftwareTarget.GRTCustomization"
807 | ModelStepFunctionPrototypeControlCompliant off
808 | CPPClassGenCompliant on
809 | AutosarCompliant off
810 | MDXCompliant off
811 | GRTInterface on
812 | GenerateAllocFcn off
813 | UseToolchainInfoCompliant on
814 | GenerateSharedConstants on
815 | CoderGroups []
816 | UseMalloc off
817 | ExtMode off
818 | ExtModeStaticAlloc off
819 | ExtModeTesting off
820 | ExtModeStaticAllocSize 1000000
821 | ExtModeTransport 0
822 | ExtModeMexFile "ext_comm"
823 | ExtModeMexArgs ""
824 | ExtModeIntrfLevel "Level1"
825 | RTWCAPISignals off
826 | RTWCAPIParams off
827 | RTWCAPIStates off
828 | RTWCAPIRootIO off
829 | GenerateASAP2 off
830 | MultiInstanceErrorCode "Error"
831 | }
832 | PropName "Components"
833 | }
834 | }
835 | SlCovCC.ConfigComp {
836 | $ObjectID 19
837 | Version "1.16.5"
838 | DisabledProps []
839 | Description "Simulink Coverage Configuration Component"
840 | Name "Simulink Coverage"
841 | CovEnable off
842 | CovScope "EntireSystem"
843 | CovIncludeTopModel on
844 | RecordCoverage off
845 | CovPath "/"
846 | CovSaveName "covdata"
847 | CovCompData ""
848 | CovMetricSettings "dwe"
849 | CovFilter ""
850 | CovHTMLOptions ""
851 | CovNameIncrementing off
852 | CovHtmlReporting off
853 | CovForceBlockReductionOff on
854 | CovEnableCumulative on
855 | CovSaveCumulativeToWorkspaceVar off
856 | CovSaveSingleToWorkspaceVar off
857 | CovCumulativeVarName "covCumulativeData"
858 | CovCumulativeReport off
859 | CovSaveOutputData on
860 | CovOutputDir "slcov_output/$ModelName$"
861 | CovDataFileName "$ModelName$_cvdata"
862 | CovShowResultsExplorer on
863 | CovReportOnPause on
864 | CovModelRefEnable "off"
865 | CovModelRefExcluded ""
866 | CovExternalEMLEnable on
867 | CovSFcnEnable on
868 | CovBoundaryAbsTol 1e-05
869 | CovBoundaryRelTol 0.01
870 | CovUseTimeInterval off
871 | CovStartTime 0
872 | CovStopTime 0
873 | }
874 | PropName "Components"
875 | }
876 | Name "Configuration"
877 | ExtraOptions ""
878 | CurrentDlgPage "Solver"
879 | ConfigPrmDlgPosition [ 180, 192, 1100, 832 ]
880 | }
881 | PropName "ConfigurationSets"
882 | }
883 | Simulink.ConfigSet {
884 | $PropName "ActiveConfigurationSet"
885 | $ObjectID 8
886 | }
887 | Object {
888 | $PropName "DataTransfer"
889 | $ObjectID 20
890 | $ClassName "Simulink.GlobalDataTransfer"
891 | DefaultTransitionBetweenSyncTasks "Ensure deterministic transfer (maximum delay)"
892 | DefaultTransitionBetweenAsyncTasks "Ensure data integrity only"
893 | DefaultTransitionBetweenContTasks "Ensure deterministic transfer (minimum delay)"
894 | DefaultExtrapolationMethodBetweenContTasks "None"
895 | AutoInsertRateTranBlk [0]
896 | }
897 | ExplicitPartitioning off
898 | BlockDefaults {
899 | ForegroundColor "black"
900 | BackgroundColor "white"
901 | DropShadow off
902 | NamePlacement "normal"
903 | FontName "Helvetica"
904 | FontSize 10
905 | FontWeight "normal"
906 | FontAngle "normal"
907 | ShowName on
908 | BlockRotation 0
909 | BlockMirror off
910 | }
911 | AnnotationDefaults {
912 | HorizontalAlignment "left"
913 | VerticalAlignment "top"
914 | ForegroundColor "black"
915 | BackgroundColor "white"
916 | DropShadow off
917 | FontName "Helvetica"
918 | FontSize 10
919 | FontWeight "normal"
920 | FontAngle "normal"
921 | UseDisplayTextAsClickCallback off
922 | }
923 | LineDefaults {
924 | FontName "Helvetica"
925 | FontSize 9
926 | FontWeight "normal"
927 | FontAngle "normal"
928 | }
929 | MaskDefaults {
930 | SelfModifiable "off"
931 | IconFrame "on"
932 | IconOpaque "opaque"
933 | RunInitForIconRedraw "off"
934 | IconRotate "none"
935 | PortRotate "default"
936 | IconUnits "autoscale"
937 | }
938 | MaskParameterDefaults {
939 | Evaluate "on"
940 | Tunable "on"
941 | NeverSave "off"
942 | Internal "off"
943 | ReadOnly "off"
944 | Enabled "on"
945 | Visible "on"
946 | ToolTip "on"
947 | }
948 | BlockParameterDefaults {
949 | Block {
950 | BlockType Constant
951 | Value "1"
952 | VectorParams1D on
953 | SamplingMode "Sample based"
954 | OutMin "[]"
955 | OutMax "[]"
956 | OutDataTypeStr "Inherit: Inherit from 'Constant value'"
957 | LockScale off
958 | SampleTime "inf"
959 | FramePeriod "inf"
960 | PreserveConstantTs off
961 | }
962 | Block {
963 | BlockType Inport
964 | Port "1"
965 | OutputFunctionCall off
966 | OutMin "[]"
967 | OutMax "[]"
968 | OutDataTypeStr "Inherit: auto"
969 | LockScale off
970 | BusOutputAsStruct off
971 | Unit "inherit"
972 | PortDimensions "-1"
973 | VarSizeSig "Inherit"
974 | SampleTime "-1"
975 | SignalType "auto"
976 | SamplingMode "auto"
977 | LatchByDelayingOutsideSignal off
978 | LatchInputForFeedbackSignals off
979 | Interpolate on
980 | }
981 | Block {
982 | BlockType Integrator
983 | ExternalReset "none"
984 | InitialConditionSource "internal"
985 | InitialCondition "0"
986 | LimitOutput off
987 | UpperSaturationLimit "inf"
988 | LowerSaturationLimit "-inf"
989 | WrapState off
990 | WrappedStateUpperValue "pi"
991 | WrappedStateLowerValue "-pi"
992 | ShowSaturationPort off
993 | ShowStatePort off
994 | AbsoluteTolerance "auto"
995 | IgnoreLimit off
996 | ZeroCross on
997 | ContinuousStateAttributes "''"
998 | }
999 | Block {
1000 | BlockType Outport
1001 | Port "1"
1002 | OutMin "[]"
1003 | OutMax "[]"
1004 | OutDataTypeStr "Inherit: auto"
1005 | LockScale off
1006 | BusOutputAsStruct off
1007 | Unit "inherit"
1008 | PortDimensions "-1"
1009 | VarSizeSig "Inherit"
1010 | SampleTime "-1"
1011 | SignalType "auto"
1012 | SamplingMode "auto"
1013 | EnsureOutportIsVirtual off
1014 | SourceOfInitialOutputValue "Dialog"
1015 | OutputWhenDisabled "held"
1016 | InitialOutput "[]"
1017 | }
1018 | Block {
1019 | BlockType SubSystem
1020 | ShowPortLabels "FromPortIcon"
1021 | Permissions "ReadWrite"
1022 | PermitHierarchicalResolution "All"
1023 | TreatAsAtomicUnit off
1024 | MinAlgLoopOccurrences off
1025 | PropExecContextOutsideSubsystem off
1026 | SystemSampleTime "-1"
1027 | RTWSystemCode "Auto"
1028 | RTWFcnNameOpts "Auto"
1029 | RTWFileNameOpts "Auto"
1030 | FunctionInterfaceSpec "void_void"
1031 | FunctionWithSeparateData off
1032 | RTWMemSecFuncInitTerm "Inherit from model"
1033 | RTWMemSecFuncExecute "Inherit from model"
1034 | RTWMemSecDataConstants "Inherit from model"
1035 | RTWMemSecDataInternal "Inherit from model"
1036 | RTWMemSecDataParameters "Inherit from model"
1037 | SimViewingDevice off
1038 | DataTypeOverride "UseLocalSettings"
1039 | DataTypeOverrideAppliesTo "AllNumericTypes"
1040 | MinMaxOverflowLogging "UseLocalSettings"
1041 | Opaque off
1042 | MaskHideContents off
1043 | SFBlockType "NONE"
1044 | GeneratePreprocessorConditionals off
1045 | PropagateVariantConditions off
1046 | TreatAsGroupedWhenPropagatingVariantConditions on
1047 | ContentPreviewEnabled off
1048 | IsWebBlock off
1049 | }
1050 | Block {
1051 | BlockType Sum
1052 | IconShape "rectangular"
1053 | Inputs "++"
1054 | CollapseMode "All dimensions"
1055 | CollapseDim "1"
1056 | InputSameDT on
1057 | AccumDataTypeStr "Inherit: Inherit via internal rule"
1058 | OutMin "[]"
1059 | OutMax "[]"
1060 | OutDataTypeStr "Inherit: Same as first input"
1061 | LockScale off
1062 | RndMeth "Floor"
1063 | SaturateOnIntegerOverflow on
1064 | SampleTime "-1"
1065 | }
1066 | }
1067 | System {
1068 | Name "demo_before"
1069 | Location [53, 52, 925, 802]
1070 | Open on
1071 | ModelBrowserVisibility off
1072 | ModelBrowserWidth 200
1073 | ScreenColor "white"
1074 | PaperOrientation "landscape"
1075 | PaperPositionMode "auto"
1076 | PaperType "usletter"
1077 | PaperUnits "inches"
1078 | TiledPaperMargins [0.500000, 0.500000, 0.500000, 0.500000]
1079 | TiledPageScale 1
1080 | ShowPageBoundaries off
1081 | ZoomFactor "100"
1082 | ReportName "simulink-default.rpt"
1083 | SIDHighWatermark "33"
1084 | Block {
1085 | BlockType Inport
1086 | Name "In1"
1087 | SID "1"
1088 | Position [195, 88, 225, 102]
1089 | ZOrder 1
1090 | IconDisplay "Port number"
1091 | }
1092 | Block {
1093 | BlockType Inport
1094 | Name "In2"
1095 | SID "2"
1096 | Position [195, 163, 225, 177]
1097 | ZOrder 2
1098 | Port "2"
1099 | IconDisplay "Port number"
1100 | }
1101 | Block {
1102 | BlockType SubSystem
1103 | Name "Subsystem"
1104 | SID "5"
1105 | Ports [2, 2]
1106 | Position [290, 56, 405, 209]
1107 | ZOrder 5
1108 | RequestExecContextInheritance off
1109 | Variant off
1110 | System {
1111 | Name "Subsystem"
1112 | Location [53, 52, 925, 802]
1113 | Open off
1114 | ModelBrowserVisibility off
1115 | ModelBrowserWidth 200
1116 | ScreenColor "white"
1117 | PaperOrientation "landscape"
1118 | PaperPositionMode "auto"
1119 | PaperType "usletter"
1120 | PaperUnits "inches"
1121 | TiledPaperMargins [0.500000, 0.500000, 0.500000, 0.500000]
1122 | TiledPageScale 1
1123 | ShowPageBoundaries off
1124 | ZoomFactor "100"
1125 | Block {
1126 | BlockType Inport
1127 | Name "In1"
1128 | SID "6"
1129 | Position [110, 103, 140, 117]
1130 | ZOrder -1
1131 | IconDisplay "Port number"
1132 | }
1133 | Block {
1134 | BlockType Inport
1135 | Name "In2"
1136 | SID "8"
1137 | Position [110, 158, 140, 172]
1138 | ZOrder 2
1139 | Port "2"
1140 | IconDisplay "Port number"
1141 | }
1142 | Block {
1143 | BlockType SubSystem
1144 | Name "Subsystem"
1145 | SID "10"
1146 | Ports [2, 2]
1147 | Position [180, 81, 320, 194]
1148 | ZOrder 3
1149 | RequestExecContextInheritance off
1150 | Variant off
1151 | System {
1152 | Name "Subsystem"
1153 | Location [53, 52, 925, 802]
1154 | Open off
1155 | ModelBrowserVisibility off
1156 | ModelBrowserWidth 200
1157 | ScreenColor "white"
1158 | PaperOrientation "landscape"
1159 | PaperPositionMode "auto"
1160 | PaperType "usletter"
1161 | PaperUnits "inches"
1162 | TiledPaperMargins [0.500000, 0.500000, 0.500000, 0.500000]
1163 | TiledPageScale 1
1164 | ShowPageBoundaries off
1165 | ZoomFactor "100"
1166 | Block {
1167 | BlockType Inport
1168 | Name "In1"
1169 | SID "11"
1170 | Position [110, 88, 140, 102]
1171 | ZOrder -1
1172 | IconDisplay "Port number"
1173 | }
1174 | Block {
1175 | BlockType Inport
1176 | Name "In2"
1177 | SID "13"
1178 | Position [110, 173, 140, 187]
1179 | ZOrder 2
1180 | Port "2"
1181 | IconDisplay "Port number"
1182 | }
1183 | Block {
1184 | BlockType Sum
1185 | Name "Add"
1186 | SID "18"
1187 | Ports [2, 1]
1188 | Position [240, 87, 270, 118]
1189 | ZOrder 6
1190 | InputSameDT off
1191 | OutDataTypeStr "Inherit: Inherit via internal rule"
1192 | SaturateOnIntegerOverflow off
1193 | }
1194 | Block {
1195 | BlockType Constant
1196 | Name "Constant"
1197 | SID "16"
1198 | Position [110, 125, 140, 155]
1199 | ZOrder 4
1200 | }
1201 | Block {
1202 | BlockType Integrator
1203 | Name "Integrator"
1204 | SID "33"
1205 | Ports [1, 1]
1206 | Position [240, 165, 270, 195]
1207 | ZOrder 8
1208 | }
1209 | Block {
1210 | BlockType Outport
1211 | Name "Out1"
1212 | SID "12"
1213 | Position [360, 98, 390, 112]
1214 | ZOrder -2
1215 | IconDisplay "Port number"
1216 | }
1217 | Block {
1218 | BlockType Outport
1219 | Name "Out2"
1220 | SID "14"
1221 | Position [360, 173, 390, 187]
1222 | ZOrder 1
1223 | Port "2"
1224 | IconDisplay "Port number"
1225 | }
1226 | Line {
1227 | ZOrder 1
1228 | SrcBlock "In1"
1229 | SrcPort 1
1230 | DstBlock "Add"
1231 | DstPort 1
1232 | }
1233 | Line {
1234 | ZOrder 2
1235 | SrcBlock "Add"
1236 | SrcPort 1
1237 | DstBlock "Out1"
1238 | DstPort 1
1239 | }
1240 | Line {
1241 | ZOrder 3
1242 | SrcBlock "Constant"
1243 | SrcPort 1
1244 | Points [43, 0; 0, -30]
1245 | DstBlock "Add"
1246 | DstPort 2
1247 | }
1248 | Line {
1249 | ZOrder 7
1250 | SrcBlock "In2"
1251 | SrcPort 1
1252 | DstBlock "Integrator"
1253 | DstPort 1
1254 | }
1255 | Line {
1256 | ZOrder 6
1257 | SrcBlock "Integrator"
1258 | SrcPort 1
1259 | DstBlock "Out2"
1260 | DstPort 1
1261 | }
1262 | }
1263 | }
1264 | Block {
1265 | BlockType Outport
1266 | Name "Out1"
1267 | SID "7"
1268 | Position [360, 103, 390, 117]
1269 | ZOrder -2
1270 | IconDisplay "Port number"
1271 | }
1272 | Block {
1273 | BlockType Outport
1274 | Name "Out2"
1275 | SID "9"
1276 | Position [360, 158, 390, 172]
1277 | ZOrder 1
1278 | Port "2"
1279 | IconDisplay "Port number"
1280 | }
1281 | Line {
1282 | ZOrder 1
1283 | SrcBlock "In1"
1284 | SrcPort 1
1285 | DstBlock "Subsystem"
1286 | DstPort 1
1287 | }
1288 | Line {
1289 | ZOrder 2
1290 | SrcBlock "In2"
1291 | SrcPort 1
1292 | DstBlock "Subsystem"
1293 | DstPort 2
1294 | }
1295 | Line {
1296 | ZOrder 3
1297 | SrcBlock "Subsystem"
1298 | SrcPort 1
1299 | DstBlock "Out1"
1300 | DstPort 1
1301 | }
1302 | Line {
1303 | ZOrder 4
1304 | SrcBlock "Subsystem"
1305 | SrcPort 2
1306 | DstBlock "Out2"
1307 | DstPort 1
1308 | }
1309 | }
1310 | }
1311 | Block {
1312 | BlockType Outport
1313 | Name "Out1"
1314 | SID "3"
1315 | Position [460, 88, 490, 102]
1316 | ZOrder 3
1317 | IconDisplay "Port number"
1318 | }
1319 | Block {
1320 | BlockType Outport
1321 | Name "Out2"
1322 | SID "4"
1323 | Position [460, 163, 490, 177]
1324 | ZOrder 4
1325 | Port "2"
1326 | IconDisplay "Port number"
1327 | }
1328 | Line {
1329 | ZOrder 1
1330 | SrcBlock "Subsystem"
1331 | SrcPort 1
1332 | DstBlock "Out1"
1333 | DstPort 1
1334 | }
1335 | Line {
1336 | ZOrder 2
1337 | SrcBlock "In1"
1338 | SrcPort 1
1339 | DstBlock "Subsystem"
1340 | DstPort 1
1341 | }
1342 | Line {
1343 | ZOrder 3
1344 | SrcBlock "In2"
1345 | SrcPort 1
1346 | DstBlock "Subsystem"
1347 | DstPort 2
1348 | }
1349 | Line {
1350 | ZOrder 4
1351 | SrcBlock "Subsystem"
1352 | SrcPort 2
1353 | DstBlock "Out2"
1354 | DstPort 1
1355 | }
1356 | }
1357 | }
1358 |
--------------------------------------------------------------------------------
/imgs/Cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/imgs/Cover.png
--------------------------------------------------------------------------------
/imgs/Cover_MathWorks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/imgs/Cover_MathWorks.png
--------------------------------------------------------------------------------
/imgs/SocialPreview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/imgs/SocialPreview.png
--------------------------------------------------------------------------------
/src/Utility/Branch.m:
--------------------------------------------------------------------------------
1 | classdef Branch < double
2 | % Branch values.
3 | % Note: We assume that the Edits tree is created with the before branch as the
4 | % first argument, and the after branch as the second argument.
5 | enumeration
6 | Left(0) % Before branch.
7 | Right(1) % After branch.
8 | Both(2) % Both branches.
9 | NotFound(-1) % Neither branch.
10 | end
11 | end
--------------------------------------------------------------------------------
/src/Utility/getInput.m:
--------------------------------------------------------------------------------
1 | function value = getInput(name, args, default)
2 | % GETINPUT Get a specific input from a list of arguments from varargin.
3 | %
4 | % Inputs:
5 | % name Char array of the input name.
6 | % args Cell array of all arguments pass in via varargin.
7 | % default Value to return if input not in list of arguments. (Optional)
8 | % Default is [].
9 | %
10 | % Outputs:
11 | % value Value of the input specified by the input name.
12 | %
13 | % Example:
14 | % >> getInput('BlockType', {'Name', 'Example', 'BlockType', 'SubSystem'})
15 | % ans =
16 | % 'SubSystem'
17 | %
18 | % >> getInput('otherFiles', {'imageFile', 'test.png', 'otherFiles', {'file1', 'file2'}})
19 | % ans =
20 | % 1x2 cell array
21 | % {'file1'} {'file2'}
22 | %
23 | % Requires: iscellcell.m
24 |
25 | if nargin == 2
26 | default = [];
27 | else
28 | assert(nargin == 3, 'Error: Expecting 2 or 3 inputs.')
29 | end
30 |
31 | if iscellcell(args)
32 | idx = find(strcmp(args, name));
33 | exists = ~isempty(idx);
34 | else
35 | args2 = cellfun(@num2str, args, 'un', 0);
36 | [exists, idx] = ismember(name, args2);
37 | end
38 |
39 | if exists && length(args) > idx
40 | value = args{idx+1};
41 | else
42 | value = default;
43 | end
44 | end
--------------------------------------------------------------------------------
/src/Utility/getNameFromPath.m:
--------------------------------------------------------------------------------
1 | function [path, name] = getNameFromPath(pathname)
2 | % GETNAMEFROMPATH Speparate the name and path of a model element's pathname.
3 | % When using the fileparts function, forwardslashes cause the name of the
4 | % element to be incorrect. This function gets the correct name of the
5 | % element, if the forwardspash is escaped.
6 | %
7 | % Inputs:
8 | % pathname Char array.
9 | %
10 | % Outputs:
11 | % path Path as a char array.
12 | % name Name of the element as a char array.
13 | %
14 | % Example:
15 | % [path_bad, name_bad] = fileparts('Comparison Root/Simulink/Subsystem/Reset 1//z blocks')
16 | % path_bad =
17 | % 'Comparison Root/Simulink/Subsystem/Reset 1/'
18 | %
19 | % name_bad =
20 | % '/z blocks'
21 | %
22 | % [path_correct, name_correct] = getNameFromPath('Comparison Root/Simulink/Subsystem/Reset 1//z blocks')
23 | % path_correct =
24 | % 'Comparison Root/Simulink/Subsystem'
25 | %
26 | % name_correct =
27 | % 'Reset 1/z blocks'
28 |
29 | name = '';
30 | i = length(pathname);
31 | while i > 1
32 | thischar = pathname(i);
33 | nextchar = pathname(i-1);
34 |
35 | if strcmp(thischar, '/') && ~strcmp(nextchar, '/') % Regular / encountered
36 | break;
37 | elseif strcmp(thischar, '/') && strcmp(nextchar, '/') % Escaped / encountered
38 | name = [thischar name]; % Add to front
39 | i = i - 2; % Skip the second /
40 | else % Regular character
41 | name = [thischar name]; % Add to front
42 | i = i - 1;
43 | end
44 | end
45 | path = pathname(1:i-1);
46 | end
--------------------------------------------------------------------------------
/src/Utility/iscellcell.m:
--------------------------------------------------------------------------------
1 | function b = iscellcell(c)
2 | % ISCELLCELL Whether the input is a cell array containing at least another cell.
3 | %
4 | % Inputs:
5 | % c Cell array.
6 | %
7 | % Outputs:
8 | % b Whether the it is a cell array of cells(1) or not(0).
9 | %
10 | % Example:
11 | % iscellcell({'a'})
12 | % ans = 0
13 | %
14 | % iscellcell({{'a'}, {'b'}})
15 | % ans = 1
16 |
17 | b = false;
18 | if iscell(c)
19 | for i = 1:length(c)
20 | if iscell(c{i})
21 | b = true;
22 | end
23 | end
24 | end
25 | end
--------------------------------------------------------------------------------
/src/find_node.m:
--------------------------------------------------------------------------------
1 | function [nodes, path] = find_node(root, varargin)
2 | % FIND_NODE Search the comparison tree for nodes.
3 | % Note1: LeftRootFile and RightRootFile must be loaded/opened.
4 | % Note2: Does not return the xmlcomp.Edits objects itself.
5 | %
6 | % Inputs:
7 | % root xmlcomp.Edits object.
8 | % varargin Search constraint as a 'Name', 'Value' pair. See below.
9 | %
10 | % Outputs:
11 | % nodes Node array of xmlcomp.Node objects.
12 | % path Cell array of node paths in the comparison tree (not
13 | % model paths).
14 | %
15 | % Usage:
16 | % nodes = FIND_NODE(ROOT, 'CONSTRAINT1', 'VALUE1', ...)
17 | % constrains the search of FIND_NODES to the specified constraint/value
18 | % pairs (not case-sensitive, except for NodeName). The following describes
19 | % the available constraint/value pairs:
20 | %
21 | % NodeType ['block' | 'line' | 'port' | 'annotation' | 'mask' | 'block_diagram' | 'stateflow' | ...]
22 | % ChangeType ['added' | 'deleted' | 'modified' | 'renamed' | 'none']
23 | % BlockType ['SubSystem' | 'Inport' | 'Outport' | ...]
24 | % StateflowType ['Stateflow.Annotation' | 'Sateflow.Transition' | 'Sateflow.State' | ...]
25 | % NodeName
26 | % FirstOnly [('off'), 'on'] For changes with 2 nodes (e.g., none, modified),
27 | % returns the first 'before' node. This is
28 | % useful for counting nodes.
29 | %
30 | % Multiple values for a single constraint can be provided via a cell array.
31 | %
32 | % Example:
33 | % >> allNodes = find_node(Edits)
34 | %
35 | % allNodes =
36 | %
37 | % 27x1 Node array with properties:
38 | %
39 | % Children
40 | % Edited
41 | % Name
42 | % Parameters
43 | % Parent
44 | % Partner
45 |
46 | % Validate inputs
47 | try
48 | assert(isa(root, 'xmlcomp.Edits'))
49 | catch
50 | message = 'Node argument must be an xmlcomp.Edits object.';
51 | error(message)
52 | end
53 |
54 | % Parse varargin
55 | changeType = lower(getInput('ChangeType', varargin));
56 | firstOnly = lower(getInput('FirstOnly', varargin, 'off'));
57 |
58 | % Find the nodes
59 | % Don't have to check both branches, depending on the ChangeType:
60 | % 1) Check RIGHT branch for Added, Modified, Renamed
61 | % 2) Check LEFT branch for Deleted, Modified, Renamed
62 | % 3) Check BOTH if no ChangeTypes or None
63 | nodesFoundLeft = [];
64 | nodesFoundRight = [];
65 | if isempty(changeType) || any(ismember(changeType, {'none', 'added', 'modified', 'renamed'}))
66 | nodesFoundRight = findNode(root.RightRoot, root, root.RightFileName, varargin);
67 | end
68 |
69 | if strcmp(firstOnly, 'off')
70 | if isempty(changeType) || any(ismember(changeType, {'none', 'deleted', 'modified', 'renamed'}))
71 | nodesFoundLeft = findNode(root.LeftRoot, root, root.LeftFileName, varargin);
72 | end
73 | else
74 | if isempty(changeType) || any(ismember(changeType, {'none', 'deleted'}))
75 | varargin{2} = {'added', 'deleted'};
76 | nodesFoundLeft = findNode(root.LeftRoot, root, root.LeftFileName, varargin);
77 | end
78 | end
79 | nodes = [nodesFoundLeft; nodesFoundRight]; % Combine node lists in case both sides were checked
80 |
81 | % Get paths of the found nodes
82 | if nargout > 1
83 | path = cell(length(nodes),1);
84 | for i = 1:length(nodes)
85 | try
86 | path{i} = getPathTree(nodes(i));
87 | catch
88 | path{i} = '';
89 | end
90 | end
91 | end
92 | end
93 |
94 | function out = findNode(node, root, file, varargin)
95 | % FINDNODE Recurse through the tree to find the node.
96 | %
97 | % Inputs:
98 | % node xmlcomp.Node object at which to start the search.
99 | % root xmlcomp.Edits object.
100 | % file RightFileName or LeftFileName to search in.
101 | % varargin Search constraints.
102 | %
103 | % Outputs:
104 | % out xmlcomp.Node objects that are found to satisfy the constraints.
105 |
106 | % Parse varargin
107 | varargin = varargin{:}; % Remove nesting cells from passing varargin in
108 | nodeType = lower(getInput('NodeType', varargin));
109 | changeType = lower(getInput('ChangeType', varargin));
110 | blockType = lower(getInput('BlockType', varargin));
111 | stateflowType = lower(getInput('StateflowType', varargin));
112 | nameValue = getInput('NodeName', varargin);
113 |
114 | % Check against varargin constraints
115 | meetsConstraints = true;
116 |
117 | if ~isempty(nodeType)
118 | if iscell(nodeType)
119 | isNodeType = ismember(nodeType, lower(getNodeType(node, file)));
120 | else
121 | isNodeType = strcmpi(nodeType, getNodeType(node, file));
122 | end
123 |
124 | if ~any(isNodeType)
125 | meetsConstraints = false;
126 | end
127 | end
128 |
129 | if ~isempty(changeType) && meetsConstraints
130 | if iscell(changeType)
131 | isChangeType = ismember(changeType, lower(getNodeChangeType(node, root)));
132 | else
133 | isChangeType = strcmpi(changeType, getNodeChangeType(node, root));
134 | end
135 |
136 | if ~any(isChangeType)
137 | meetsConstraints = false;
138 | end
139 | end
140 |
141 | if ~isempty(blockType) && meetsConstraints
142 | if iscell(blockType)
143 | isBlockType = ismember(blockType, lower(getNodeBlockType(node, file)));
144 | else
145 | isBlockType = strcmpi(blockType, getNodeBlockType(node, file));
146 | end
147 |
148 | if ~any(isBlockType)
149 | meetsConstraints = false;
150 | end
151 | end
152 |
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 |
165 | if ~isempty(nameValue) && meetsConstraints
166 | if iscell(nameValue)
167 | isMatchedName = ismember(nameValue, node.Name);
168 | else
169 | isMatchedName = strcmp(nameValue, node.Name);
170 | end
171 |
172 | if ~any(isMatchedName)
173 | meetsConstraints = false;
174 | end
175 | end
176 |
177 | if meetsConstraints
178 | out = node;
179 | else
180 | out = {};
181 | end
182 |
183 | if ~hasChildren(node)
184 | return;
185 | end
186 |
187 | for i = 1:length(node.Children)
188 | out = [out; findNode(node.Children(i), root, file, varargin)];
189 | end
190 | end
--------------------------------------------------------------------------------
/src/getBranch.m:
--------------------------------------------------------------------------------
1 | function branch = getBranch(node, root)
2 | % GETBRANCH Find which branch of the tree the node is located in.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object or xmlcomp.Node array.
6 | % root xmlcomp.Edits object.
7 | %
8 | % Outputs:
9 | % branch Branch the node is in: left(0), right(1), both(2),
10 | % or not found (-1).
11 | %
12 | % Example:
13 | % >> getBranch(Edits.LeftRoot.Children.Children.Children.Children(1), Edits)
14 | %
15 | % ans =
16 | %
17 | % 1
18 |
19 | % Validate inputs
20 | try
21 | assert(isa(node, 'xmlcomp.Node'))
22 | catch
23 | message = 'Node argument must be an xmlcomp.Node object.';
24 | error(message)
25 | end
26 |
27 | try
28 | assert(isa(root, 'xmlcomp.Edits'))
29 | catch
30 | message = 'Root argument must be an xmlcomp.Edits object.';
31 | error(message)
32 | end
33 |
34 | branch = Branch.NotFound * ones(size(node)); % Default is 'not found'
35 |
36 | for i = 1:length(node)
37 | % Search in both branches
38 | inLeft = existsNode(node(i), root.LeftRoot);
39 | inRight = existsNode(node(i), root.RightRoot);
40 |
41 | if inLeft && inRight
42 | branch(i) = Branch.Both;
43 | elseif inRight
44 | branch(i) = Branch.Right;
45 | elseif inLeft
46 | branch(i) = Branch.Left;
47 | else
48 | branch(i) = Branch.NotFound;
49 | end
50 | end
51 | end
52 |
53 | function found = existsNode(node, branchNode)
54 | % EXISTSNODE Determine the existence of the node in the tree branch.
55 | %
56 | % Inputs:
57 | % node xmlcomp.Node object.
58 | % branchNode LeftRoot or RightRoot of xmlcomp.Edits object.
59 | %
60 | % Outputs:
61 | % found Whether the node is found(1) or not(0).
62 |
63 | % Exact match
64 | found = (node == branchNode);
65 | if found
66 | return
67 | end
68 |
69 | % Non-exact match for modified elements
70 | found = nodecmp(node, branchNode);
71 | if found
72 | return
73 | end
74 |
75 | % Base case
76 | if ~hasChildren(branchNode)
77 | return
78 | end
79 |
80 | % Recursive case
81 | for i = 1:length(branchNode.Children)
82 | found = existsNode(node, branchNode.Children(i));
83 | if found
84 | return
85 | end
86 | end
87 | end
--------------------------------------------------------------------------------
/src/getFile.m:
--------------------------------------------------------------------------------
1 | function files = getFile(node, root)
2 | % GETFILE Find which file the node is located in.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object or xmlcomp.Node array.
6 | % root xmlcomp.Edits object.
7 | %
8 | % Outputs:
9 | % files One or more file paths.
10 |
11 | branch = getBranch(node, root);
12 |
13 | if branch == Branch.Left
14 | files = root.LeftFileName;
15 | elseif branch == Branch.Right
16 | files = root.RightFileName;
17 | elseif branch == Branch.Both
18 | files = {root.LeftFileName; root.RightFileName};
19 | else
20 | files = [];
21 | end
22 | end
--------------------------------------------------------------------------------
/src/getHandle.m:
--------------------------------------------------------------------------------
1 | function hdl = getHandle(node, sys)
2 | % GETHANDLE Get the handle of the model element associated with the node from
3 | % the comparison.
4 | %
5 | % Note: Not all nodes have an associated handle (e.g., Mask, Comparison Root).
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object for which to find the handle.
9 | % sys Path or name of the model.
10 | %
11 | % Outputs:
12 | % hdl Handle of node in the model.
13 |
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | narginchk(2,2);
22 |
23 | hdl = []; % Default return value when there are no matches
24 |
25 | type = getNodeType(node, sys);
26 | if strcmp(type, 'block')
27 | p = getPath(node, sys);
28 | if ~isempty(p)
29 | hdl = get_param(p, 'Handle');
30 | else
31 | hdl = [];
32 | end
33 | elseif strcmp(type, 'line')
34 | % -- Search for the line in the model by matching sources, destinations, and parents --
35 | % Get all lines in the subsystem
36 | p = getParentPath(node, sys);
37 | lines = find_system(p, 'SearchDepth', '1', 'FindAll', 'on', 'Type', 'line');
38 |
39 | % Get the node's parent
40 | parent = node.Parent;
41 | while isLine(parent)
42 | parent = parent.Parent;
43 | end
44 | parentName_node = parent.Name;
45 |
46 | % Get the node's source/destination parameters
47 | try
48 | if any(strcmp({node.Parameters.Name}, 'SrcBlock'))
49 | srcBlock_node = node.Parameters(strcmp({node.Parameters.Name}, 'SrcBlock')).Value;
50 | else
51 | srcBlock_node = '';
52 | end
53 | catch % line branch
54 | srcBlock_node = '';
55 | end
56 |
57 | try
58 | if any(strcmp({node.Parameters.Name}, 'SrcPort'))
59 | srcPort_node = node.Parameters(strcmp({node.Parameters.Name}, 'SrcPort')).Value;
60 | else
61 | srcPort_node = '';
62 | end
63 | catch % line branch
64 | srcPort_node = '';
65 | end
66 |
67 | try
68 | if any(strcmp({node.Parameters.Name}, 'DstBlock'))
69 | dstBlock_node = node.Parameters(strcmp({node.Parameters.Name}, 'DstBlock')).Value;
70 | else
71 | dstBlock_node = '';
72 | end
73 | catch % line branch
74 | dstBlock_node = '';
75 | end
76 |
77 | try
78 | if any(strcmp({node.Parameters.Name}, 'DstPort'))
79 | dstPort_node = node.Parameters(strcmp({node.Parameters.Name}, 'DstPort')).Value;
80 | else
81 | dstPort_node = '';
82 | end
83 | catch % line branch
84 | dstPort_node = '';
85 | end
86 |
87 | for i = 1:length(lines)
88 | if get_param(lines(i), 'LineParent') == -1
89 | % Line source is a block
90 | try
91 | srcHdl = get_param(lines(i), 'SrcPortHandle');
92 |
93 | [~, srcBlock, ~] = fileparts(get_param(srcHdl, 'Parent'));
94 | srcPort = num2str(get_param(srcHdl, 'PortNumber'));
95 | catch
96 | % Unconnected line, with no source
97 | srcBlock = '';
98 | srcPort = '';
99 | end
100 | else
101 | % Line source is a line
102 | srcBlock = '';
103 | srcPort = '';
104 | end
105 | [~, parentName, ~] = fileparts(get_param(lines(i), 'Parent'));
106 |
107 | try
108 | % Line destination is a block
109 | dstHdl = get_param(lines(i), 'DstPortHandle');
110 | [~, dstBlock, ~] = fileparts(get_param(dstHdl, 'Parent'));
111 | dstPort = num2str(get_param(dstHdl, 'PortNumber'));
112 | catch
113 | % Line destination is multiple lines (i.e., it's a line trunk)
114 | dstBlock = '';
115 | dstPort = '';
116 | end
117 |
118 | % Prints for troubleshooting
119 | %fprintf('%s:%s -> %s:%s', srcBlock_node, srcPort_node, dstBlock_node, dstPort_node)
120 | %fprintf(' == %s:%s -> %s:%s ', srcBlock, srcPort, dstBlock, dstPort);
121 | %fprintf(' %s == %s\n', parentName_node, parentName);
122 |
123 | sameParent = strcmp(parentName, parentName_node);
124 | sameSrc = strcmp(srcBlock, srcBlock_node);
125 | sameSrcPort = strcmp(srcPort, srcPort_node);
126 | sameDst = strcmp(dstBlock, dstBlock_node);
127 | sameDestPort = strcmp(dstPort, dstPort_node);
128 |
129 | % Check for match
130 | if sameSrc && sameSrcPort && sameDst && sameDestPort && sameParent
131 | hdl = lines(i);
132 | return
133 | end
134 | end
135 | elseif strcmp(type, 'annotation')
136 | % -- Search for the annotation in the model --
137 | % Get all annotations in the subsystem
138 | p = getParentPath(node, sys);
139 | annotations = find_system(p, 'SearchDepth', '1', 'LookUnderMasks', 'on', 'FindAll', 'on', 'Type', 'annotation');
140 |
141 | % Compare the node's Name param with the annotations' Text params
142 | try
143 | nameOfAnnotation = node.Parameters(strcmp({node.Parameters.Name}, 'Name')).Value;
144 | catch
145 | nameOfAnnotation = node.Name;
146 | end
147 |
148 | % The node name can be truncated and ... or .. added to
149 | % the end if it is long, so we need to accomodate for
150 | % semi-matching names
151 | if endsWith(nameOfAnnotation, '...')
152 | a = nameOfAnnotation;
153 | nameOfAnnotation = a(1:end-3);
154 | elseif endsWith(nameOfAnnotation, '..')
155 | a = nameOfAnnotation;
156 | nameOfAnnotation = a(1:end-2);
157 | end
158 |
159 | for i = 1:length(annotations)
160 | % Check for match
161 | if startsWith(get_param(annotations(i), 'PlainText'), nameOfAnnotation) || ...
162 | startsWith(get_param(annotations(i), 'Text'), nameOfAnnotation)
163 | hdl = annotations(i);
164 | return
165 | end
166 | end
167 | elseif strcmp(type, 'port')
168 | % -- Look for the port in the model --
169 | % Get all ports in the subsystem
170 | p = getParentPath(node, sys);
171 | ports = find_system(p, 'SearchDepth', '1', 'FindAll', 'on', 'Type', 'port');
172 |
173 | % Compare the node's params with the ports' params
174 | try
175 | paramNames = {node.Parameters.Name};
176 | paramValues = {node.Parameters.Value};
177 | catch ME
178 | if strcmp(ME.identifier, 'MATLAB:structRefFromNonStruct')
179 | % There are no parameters/values
180 | paramNames = '';
181 | paramValues = '';
182 | else
183 | rethrow(ME);
184 | end
185 | end
186 |
187 | for i = 1:length(ports) % For each port
188 | allParamsSame = true;
189 | for j = 1:length(paramNames) % For each of its params
190 | % Param values can be strings, numbers, etc., so we need to
191 | % compare them appropriately with strcmp or eq
192 | % TODO: Test with param with vector values like Position
193 | [num, isNum] = str2num(char(paramValues(j)));
194 | if isNum
195 | if get_param(ports(i), char(paramNames(j))) ~= num
196 | allParamsSame = false;
197 | break % Don't check the rest
198 | end
199 | else
200 | if ~strcmp(get_param(ports(i), char(paramNames(j))), char(paramValues(j)))
201 | allParamsSame = false;
202 | break % Don't check the rest
203 | end
204 | end
205 | end
206 | if allParamsSame
207 | hdl = ports(i);
208 | return
209 | 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
218 | end
219 | end
--------------------------------------------------------------------------------
/src/getNode.m:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/McSCert/Model-Comparison-Utility/e2409bd2957686a1f9e348bde75ae7f35a7fdd54/src/getNode.m
--------------------------------------------------------------------------------
/src/getParentPath.m:
--------------------------------------------------------------------------------
1 | function path = getParentPath(node, sys)
2 | % GETPARENTPATH Get the path of the parent element containing the node
3 | % in a model (usually a subsystem, or the root system). This is non-trivial for
4 | % nodes representing lines, hence this function. Note: The parent of a port is the source block.
5 | %
6 | % Inputs:
7 | % node xmlcomp.Node object.
8 | % sys Path or name of the model.
9 | %
10 | % Outputs:
11 | % path Path of the parent.
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | if isLine(node)
22 | % Line segments have the tree trunk as parents, so trace through them
23 | % all until we hit a block
24 | parent = node.Parent;
25 | while isLine(parent)
26 | parent = parent.Parent;
27 | end
28 | else
29 | parent = node.Parent;
30 | end
31 |
32 | path = getPath(parent, char(sys));
33 |
34 | % Deal with duplicated elements in earlier versions
35 | if strcmp(version('-release'), '2016b') && isempty(path)
36 | if strcmp(parent.Name, parent.Parent.Name)
37 | path = getPath(parent.Parent, char(sys));
38 | end
39 | end
40 | end
--------------------------------------------------------------------------------
/src/getPath.m:
--------------------------------------------------------------------------------
1 | function path = getPath(node, sys)
2 | % 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, Sateflow transitions).
4 | % If an empty cell array is returned, no valid path has been found.
5 | %
6 | % Inputs:
7 | % node xmlcomp.Node object, representing a block.
8 | % sys Path or name of the model.
9 | %
10 | % Outputs:
11 | % path Path of node in the model.
12 | %
13 | % Examples:
14 | % getPath(Edits.LeftRoot.Children.Children.Children.Children(1), 'demo_before')
15 | % ans =
16 | % demo_defore/Subsystem/Subsystem/Add
17 |
18 | % Validate inputs
19 | try
20 | assert(isa(node, 'xmlcomp.Node'))
21 | catch
22 | message = 'Node argument must be an xmlcomp.Node object.';
23 | error(message)
24 | end
25 |
26 | sys = char(sys);
27 | if isStateflow(node)
28 | node = getStateflowParent(node);
29 | end
30 | path = assemblePath(sys, node);
31 |
32 | % Address R2016b bug
33 | if strcmp(version('-release'), '2016b')
34 | path = fixDuplicates(path);
35 | end
36 |
37 | % Parent is root system
38 | if strcmp(path, sys)
39 | return
40 | else
41 | % Confirm that the model does in fact have an element with this path
42 | try
43 | [~,name,~] = fileparts(sys);
44 | root = bdroot(name);
45 | sysLoaded = bdIsLoaded(root);
46 | if sysLoaded && (getSimulinkBlockHandle(path) == -1)
47 | try
48 | % Try to see if it's an annotation at the path
49 | [pathOfAnnotation, nameOfAnnotation] = getNameFromPath(path);
50 | annotationsInPath_h = find_system(pathOfAnnotation, 'SearchDepth', 1, 'LookUnderMasks', 'on', 'FindAll', 'on', 'Type', 'annotation');
51 | annotationsInPath_names = get_param(annotationsInPath_h, 'Name');
52 |
53 | % Make it a cell array for consistency
54 | if ~iscell(annotationsInPath_names)
55 | annotationsInPath_names = {annotationsInPath_names};
56 | end
57 |
58 | % The comparison node name can be truncated and .. added to
59 | % the end if it is long, so we need to accomodate for
60 | % semi-matching names
61 | if endsWith(nameOfAnnotation, '...')
62 | a = nameOfAnnotation;
63 | nameOfAnnotation = a(1:end-3);
64 | elseif endsWith(nameOfAnnotation, '..')
65 | a = nameOfAnnotation;
66 | nameOfAnnotation = a(1:end-2);
67 | end
68 |
69 | actual_name = '';
70 | for i = 1:length(annotationsInPath_names)
71 | if startsWith(annotationsInPath_names{i}, nameOfAnnotation)
72 | actual_name = annotationsInPath_names{i};
73 | break;
74 | end
75 | end
76 | if iscell(actual_name)
77 | actual_name = actual_name{:};
78 | end
79 | actual_name = strrep(actual_name, '/', '//'); % Escape forwardslashes in names
80 | path = [pathOfAnnotation '/' actual_name];
81 | catch
82 | path = {};
83 | end
84 | end
85 | catch
86 | % Model not loaded so cannot check
87 | warning('off', 'backtrace');
88 | message = ['Model ' name ' is not loaded. Could not check if ' path ...
89 | ' exists in system ' sys '. Path returned is a best guess based'...
90 | ' on the xmlcomp.Edits object.' newline];
91 | warning(message)
92 | end
93 | end
94 | end
95 |
96 | function path = assemblePath(sys, node)
97 | % ASSEMBLEPATH Recursively assemble the path string from the xmlcomp.Edits object.
98 | % Any comparison artifacts are omitted (e.g. 'Comparison Root').
99 | %
100 | % Inputs:
101 | % sys Path or name of the model.
102 | % node xmlcomp.Node object, representing a block, for which to find the path.
103 | %
104 | % Outputs:
105 | % path Path of the node.
106 |
107 | % Get path within the tree first.
108 | basePath = getPathTree(node);
109 |
110 | % All valid paths representing a block start with one of the following:
111 | initPath1 = 'SLX Comparison Root/Simulink/System/'; % For older MATLAB versions
112 | initPath2 = 'Comparison Root/Simulink/'; % For newer MATLAB versions
113 |
114 | % Notes about invalid block paths:
115 | %
116 | % The following paths are invalid:
117 | % 'SLX Comparison Root/Simulink/System'
118 | % 'Comparison Root/Simulink'
119 | % 'SLX Comparison Root/Simulink'
120 | % 'SLX Comparison Root'
121 | % 'Comparison Root'
122 | % Paths starting with the following are invalid:
123 | % 'Comparison Root/Model Configuration Sets'
124 | %
125 | % This is likely not comprehensive and may change in future versions of
126 | % MATLAB.
127 |
128 | startIdx = regexp(basePath, ['^(' initPath1 '|' initPath2 ')'], 'end', 'once');
129 | if isempty(startIdx)
130 | % Path does not represent a block.
131 | path = '';
132 | else
133 | % Path represents a block.
134 | startIdx = startIdx + 1;
135 |
136 | [~,name,~] = fileparts(sys);
137 | path = [name '/' basePath(startIdx:end)];
138 | end
139 | end
140 |
141 | function pathafter = fixDuplicates(pathbefore)
142 | % FIXDUPLICATES Removes duplicated elements in the Edits tree, caused due to
143 | % a bug in R2016b. Specifically, removes duplicated Subsystems and
144 | % Simulink Functions.
145 | %
146 | % Inputs:
147 | % pathbefore Original path.
148 | %
149 | % Outputs:
150 | % pathafter Modified path.
151 | %
152 | % Examples:
153 | % fixDuplicates('test1_diff_2016b/Subsystem/Subsystem/Subsystem/Subsystem/Add')
154 | % ans =
155 | % test1_diff_2016b/Subsystem/Subsystem/Add
156 |
157 | % Relevant observations:
158 | %
159 | % Undesirable duplicates occur after parts(1) and if another index is not a
160 | % duplicate, then it is assumed that there are no more duplicates.
161 | % Examples:
162 | % a/a/b -> a/a/b
163 | % a/a/a/b -> a/a/b
164 | % a/b/b/c/c/d/e/e/e -> a/b/c/d/e/e/e
165 | % a/b/b/c/c/c/d/d/d -> a/b/c/c/d/d/d
166 | %
167 | % Subsystems in paths are separated by a single '/' with no adjacent '/'.
168 |
169 | if ~isempty(pathbefore)
170 | splitIdxs1 = regexp(pathbefore, '[^/]/') + 1;
171 | splitIdxs2 = regexp(pathbefore, '/[^/]');
172 | splitIdxs = intersect(splitIdxs1, splitIdxs2); % Positions of '/' with no '/' immediately before or after (pattern '[^/]/[^/]' would fail for 'a/a/a')
173 | splitIdxs = [0 splitIdxs length(pathbefore)+1]; % Also 'split' at start and end
174 | parts = {};
175 | for i = 1:length(splitIdxs)-1
176 | start = splitIdxs(i)+1;
177 | stop = splitIdxs(i+1)-1;
178 | parts{i} = pathbefore(start:stop);
179 | end
180 |
181 | partsafter = parts;
182 | i = 2;
183 | flag = true;
184 | while flag
185 | if i+1 <= length(partsafter)
186 | if strcmp(partsafter{i}, partsafter{i+1})
187 | % Remove duplicate
188 | partsafter(i+1) = [];
189 | i = i+1;
190 | else
191 | % Keep the rest
192 | flag = false;
193 | end
194 | else
195 | % Keep the rest
196 | flag = false;
197 | end
198 | end
199 |
200 | pathafter = strjoin(partsafter, '/');
201 | else
202 | pathafter = pathbefore;
203 | end
204 | end
--------------------------------------------------------------------------------
/src/getPathTree.m:
--------------------------------------------------------------------------------
1 | function path = getPathTree(node)
2 | % GETPATHTREE Get the path of the node in the comparison tree.
3 | % Note: Cannot go back past 'Comparison Root' because these is no Parent
4 | % link between 'Comparison Root' and the Edits object.
5 | %
6 | % Whereas the getPath function gets the path in the model, this function
7 | % gets the path in tree, and shows the tree-specific nodes (e.g.
8 | % Comparison Root)
9 | %
10 | % Inputs:
11 | % node xmlcomp.Node object for which to find the path.
12 | %
13 | % Outputs:
14 | % path Path of node in the comparison tree.
15 |
16 | % Validate inputs
17 | try
18 | assert(isa(node, 'xmlcomp.Node'))
19 | catch
20 | message = 'Node argument must be an xmlcomp.Node object.';
21 | error(message)
22 | end
23 |
24 | % Go up through the parents
25 | node_i = node;
26 | path = strrep(node.Name, '/', '//');
27 | while ~isempty(node_i.Parent)
28 | parentNameInSimPath = strrep(node_i.Parent.Name, '/', '//');
29 | path = [parentNameInSimPath '/' path]; % Add to path
30 | node_i = node_i.Parent; % Goto next
31 | end
32 | end
--------------------------------------------------------------------------------
/src/getStateflowObj.m:
--------------------------------------------------------------------------------
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:
--------------------------------------------------------------------------------
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/getSubTree.m:
--------------------------------------------------------------------------------
1 | function out = getSubTree(node)
2 | % GETSUBTREE Get the nodes of the subtree starting at some root node.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % out Node array.
9 |
10 | out = node;
11 |
12 | if ~hasChildren(node)
13 | return;
14 | end
15 |
16 | for i = 1:length(node.Children)
17 | out = [out; getSubTree(node.Children(i))];
18 | end
19 | end
--------------------------------------------------------------------------------
/src/hasChildren.m:
--------------------------------------------------------------------------------
1 | function c = hasChildren(node)
2 | % HASCHILDREN Determine if the node has children.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % c Whether the node has children(1) or not(0).
9 |
10 | try
11 | c = ~isempty(node.Children);
12 | catch
13 | c = false;
14 | end
15 | end
--------------------------------------------------------------------------------
/src/hasParent.m:
--------------------------------------------------------------------------------
1 | function p = hasParent(node)
2 | % HASPARENT Determine if the node has a parent.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % c Whether the node has a parent(1) or not(0).
9 |
10 | try
11 | p = ~isempty(node.Parent);
12 | catch
13 | p = false;
14 | end
15 | end
--------------------------------------------------------------------------------
/src/highlight/highlightNodes.m:
--------------------------------------------------------------------------------
1 | function highlightNodes(nodes, sys, varargin)
2 | % HIGHLIGHTNODES Color model elements corresponding to nodes from the comparison tree.
3 | %
4 | % Inputs:
5 | % nodes xmlcomp.Edits objects or handles.
6 | % sys Path or name of the model.
7 | % varargin:
8 | % fg Matlab color for foreground. Default is red.
9 | % bg Matlab color for background. Default is yellow.
10 | % method Use hilite_system(0) or set the BackgroundColor
11 | % parameters(1). Note that hilite_system will color SubSystem blocks
12 | % if their CONTENTS are changed, not necessarily when the SubSystem
13 | % itself is changed. See comparison below.
14 | %
15 | % Outputs:
16 | % N/A
17 | %
18 | % HIGHLIGHTING METHOD COMPARISON
19 | % --------------------------------
20 | % 0. Hilite
21 | % - Highlights SubSystem blocks when they are not modified themselves
22 | % - Disappears upon model close
23 | % - Does not overwrite existing coloring
24 | % * To undo, right-click in the model and select 'Remove Highlighting'
25 | % - Can do highlighting on a loaded model as well as an opened model
26 | %
27 | % 1. Parameter Setting (Default)
28 | % - Only highlights SubSystem blocks when they are listed in the nodes argument
29 | % - Only changes BackgroundColor. As a result, lines are not highlighted.
30 | % - Warning: Changing the ForegroundColor of blocks also colors their
31 | % outgoing lines! This will show inaccurate results.
32 | % - Can be saved in the model
33 | % - Overwrites previous coloring without the ability to undo
34 | % * To revert to previous highlighting, do not save, and then close and
35 | % reopen the model
36 | % * To revert to no highlighting (i.e. default black/white), run:
37 | % highlightNodes(nodes, sys, 'fg', 'black', 'bg', 'white')
38 | % - Can do highlighting on an opened model only
39 |
40 | try
41 | assert(strcmp(get_param(bdroot(sys), 'Lock'), 'off'));
42 | catch ME
43 | if strcmp(ME.identifier, 'MATLAB:assert:failed') || ...
44 | strcmp(ME.identifier, 'MATLAB:assertion:failed')
45 | error('Library is locked.')
46 | end
47 | end
48 |
49 | % Get inputs
50 | fgColor = getInput('fg', varargin);
51 | bgColor = getInput('bg', varargin);
52 | method = getInput('method', varargin);
53 |
54 | if method
55 | assert(bdIsLoaded(sys) && strcmp(get_param(sys, 'Shown'), 'on'), 'Model is not opened.');
56 | else
57 | assert(bdIsLoaded(sys), 'Model is not loaded.');
58 | end
59 |
60 | if isempty(fgColor)
61 | fgColor = 'red';
62 | end
63 | if isempty(bgColor)
64 | bgColor = 'yellow';
65 | end
66 |
67 | % If given Nodes instead of handles, get the handles and/or Stateflow
68 | % objects
69 | if isa(nodes, 'xmlcomp.Node')
70 | hdls = zeros(1, length(nodes));
71 | sfObj = [];
72 | for i = 1:length(nodes)
73 | try
74 | hdls(i) = getHandle(nodes(i), sys);
75 | catch
76 | hdls(i) = NaN;
77 | end
78 |
79 | if isStateflow(nodes(i))
80 | sfObj = vertcat(sfObj, getStateflowObj(nodes(i)));
81 | end
82 | end
83 | nodes = hdls(isfinite(hdls(:))); % Remove invalid hdls
84 | end
85 |
86 | % %Highlight Simulink
87 | if method
88 | colorRegular(nodes, fgColor, bgColor);
89 | else
90 | set_param(0, 'HiliteAncestorsData', ...
91 | struct('HiliteType', 'user2', ...
92 | 'ForegroundColor', fgColor, ...
93 | 'BackgroundColor', bgColor));
94 | hilite_system_notopen(nodes, 'user2');
95 | 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
104 | end
105 |
106 | function colorRegular(hdls, fg, bg)
107 | % COLORREGULAR Color model objects by setting their BackgroundColor
108 | % parameter.
109 | %
110 | % Inputs:
111 | % hdls Handle of model element.
112 | % fg Matlab color for foreground.
113 | % bg Matlab color for background.
114 | %
115 | % Outputs:
116 | % N/A
117 |
118 | for i = 1:length(hdls)
119 | try
120 | set_param(hdls(i), 'BackgroundColor', bg);
121 | catch
122 | % hdl is not valid or does not have this parameter
123 | end
124 |
125 | % Warning: Chaning the ForegroundColor of a block will also color all
126 | % outgoing lines!
127 | %try
128 | % set_param(hdls(i), 'ForegroundColor', fg);
129 | %catch
130 | % % hdl is not valid of does not have this parameter
131 | %end
132 | end
133 | end
--------------------------------------------------------------------------------
/src/highlight/hilite_system_notopen.m:
--------------------------------------------------------------------------------
1 | function hilite_system_notopen(sys,hilite,varargin)
2 | % HILITE_SYSTEM_NOTOPEN Highlight a Simulink object.
3 | % This is a customized version of the built-in Matlab function hihlite_system.
4 | %
5 | % HILITE_SYSTEM_NOTOPEN(SYS) highlights a Simulink object by WITHOUT opening the system
6 | % window that contains the object and then highlighting the object using the
7 | % HiliteAncestors property. This is a modification of the original function,
8 | % described below:
9 | %
10 | % HILITE_SYSTEM_NOTOPEN(SYS) highlights a Simulink object by first opening the system
11 | % window that contains the object and then highlighting the object using the
12 | % HiliteAncestors property.
13 | %
14 | % You can specify the highlighting options as additional right hand side
15 | % arguments to HILITE_SYSTEM_NOTOPEN. Options include:
16 | %
17 | % default highlight with the 'default' highlight scheme
18 | % none turn off highlighting
19 | % find highlight with the 'find' highlight scheme
20 | % unique highlight with the 'unique' highlight scheme
21 | % different highlight with the 'different' highlight scheme
22 | % user1 highlight with the 'user1' highlight scheme
23 | % user2 highlight with the 'user2' highlight scheme
24 | % user3 highlight with the 'user3' highlight scheme
25 | % user4 highlight with the 'user4' highlight scheme
26 | % user5 highlight with the 'user5' highlight scheme
27 | %
28 | % To alter the colors of a highlighting scheme, use the following command:
29 | %
30 | % set_param(0, 'HiliteAncestorsData', HILITEDATA)
31 | %
32 | % where HILITEDATA is a MATLAB structure array with the following fields:
33 | %
34 | % HiliteType one of the highlighting schemes listed above
35 | % ForegroundColor a color string (listed below)
36 | % BackgroundColor a color string (listed below)
37 | %
38 | % Available colors to set are 'black', 'white', 'red', 'green', 'blue',
39 | % 'yellow', 'magenta', 'cyan', 'gray', 'orange', 'lightBlue', and
40 | % 'darkGreen'.
41 | %
42 | % Examples:
43 | %
44 | % % highlight the subsystem 'f14/Controller/Stick Prefilter'
45 | % HILITE_SYSTEM_NOTOPEN('f14/Controller/Stick Prefilter')
46 | %
47 | % % highlight the subsystem 'f14/Controller/Stick Prefilter'
48 | % % in the 'error' highlighting scheme.
49 | % HILITE_SYSTEM_NOTOPEN('f14/Controller/Stick Prefilter', 'error')
50 | %
51 | % See also OPEN_SYSTEM, FIND_SYSTEM, SET_PARAM
52 |
53 | % Copyright 1990-2009 The MathWorks, Inc.
54 | % $Revision: 1.9.2.7 $
55 |
56 | % Massage the input data for easier management below:
57 | % chars --> cell arrays
58 | % scalar --> vector of length 2 with repeate of scalar (a hack, yes, but
59 | % it does make things simpler...)
60 | if ischar(sys)
61 | sys = { sys, sys };
62 | elseif iscell(sys) && (length(sys) == 1)
63 | sys = {cell2mat(sys(1)), cell2mat(sys(1))};
64 | elseif isreal(sys) && (length(sys) == 1)
65 | sys = [sys sys];
66 | end
67 |
68 | % Use handles instead of strings to simplify the code
69 | sys = get_param(sys, 'Handle');
70 | sys = [sys{:}];
71 |
72 | % Unfortunately, port highlighting is currently not
73 | % supported. If possible, we use a connected segment.
74 | % Otherwise, return unsuccessfully.
75 | ports = find(strcmp(get_param(sys,'type'),'port'));
76 | if(~isempty(ports))
77 | portLines = get_param(sys(ports),'Line');
78 | if(~eq(portLines{1}, -1) && ~eq(portLines{2},-1))
79 | if iscell(portLines)
80 | sys(ports) = [ portLines{:} ];
81 | else
82 | sys(ports) = portLines;
83 | end
84 | else
85 | return;
86 | end
87 | end
88 |
89 | % Construct a list of parent windows for each of the specified objects
90 | parents = get_param(sys,'Parent');
91 |
92 | % Discard objects with no parent. They are models
93 | mdls = find(strcmp(parents, ''));
94 | parents(mdls) = [];
95 | sys(mdls) = [];
96 |
97 | % Set the HiliteAncestors property for each of the blocks
98 | if nargin == 1
99 | hilite = 'on';
100 | end
101 |
102 | hiliteArgs = {'HiliteAncestors', hilite};
103 |
104 | % For each 'sys', set the HiliteAncestors property
105 | for i = 1:length(sys)
106 | set_param(sys(i), hiliteArgs{:}, varargin{:});
107 | end
108 |
109 | % Scroll and zoom the window so that our objects are visible. Don't allow
110 | % the window to zoom out to more than 100% (for backwards compatibility).
111 | % Simulink.scrollToVisible(sys,false);
--------------------------------------------------------------------------------
/src/isChildrenModified.m:
--------------------------------------------------------------------------------
1 | function out = isChildrenModified(node, root)
2 | % ISCHILDRENMODIFIED Check if the subsystem contains
3 | % added/deleted/modified/renamed direct child elements. Does not
4 | % recursively check a subsystem's children's children, etc.
5 | %
6 | % Inputs:
7 | % node xmlcomp.Node object.
8 | % root xmlcomp.Edits object.
9 | %
10 | % Outputs:
11 | % out Whether the contents have been modified(1) or not(0).
12 |
13 | out = false;
14 |
15 | for i = 1:length(node.Children)
16 | child = node.Children(i);
17 |
18 | if isPort(child) % Skip ports because they aren't useful
19 | continue
20 | end
21 |
22 | if isModified(child)
23 | out = true;
24 | %fprintf('Modified: %s (Child %i)\n', child.Name, i)
25 | return
26 | elseif isRenamed(child)
27 | out = true;
28 | %fprintf('Renamed: %s (Child %i)\n', child.Name, i)
29 | return
30 | elseif isAdded(child, root)
31 | out = true;
32 | %fprintf('Added: %s (Child %i)\n', child.Name, i)
33 | return
34 | elseif isDeleted(child, root)
35 | out = true;
36 | %fprintf('Deleted: %s (Child %i)\n', child.Name, i)
37 | return
38 | end
39 | end
40 | end
--------------------------------------------------------------------------------
/src/nodecmp.m:
--------------------------------------------------------------------------------
1 | function sameNodes = nodecmp(node1, node2)
2 | % NODECMP Determine if two nodes are the same element before and after a change.
3 | % This method shold NOT BE USED to show exact equality (e.g. that node1
4 | % equals node1); please use the eq (==) method instead. This function is to show
5 | % that two nodes before and after changes represent the same element. This is
6 | % not the same as exact equality because the nodes may have different parameter
7 | % values as a result of the changes.
8 | %
9 | % Inputs:
10 | % node1 xmlcomp.Node object.
11 | % node2 xmlcomp.Node object.
12 | %
13 | % Outputs:
14 | % sameNodes Whether the nodes are the same(1) or not(0).
15 |
16 | % Validate inputs
17 | try
18 | assert(isa(node1, 'xmlcomp.Node') && isa(node2, 'xmlcomp.Node'))
19 | catch
20 | message = 'Node argument must be an xmlcomp.Node object.';
21 | error(message)
22 | end
23 |
24 | sameNodes = false;
25 |
26 | % -- Check names --
27 | if ~(strcmp(node1.Name, node2.Name))
28 | return
29 | end
30 |
31 | % -- Check edited --
32 | if ~(node1.Edited == node2.Edited)
33 | return
34 | end
35 |
36 | % -- Check parameters --
37 | % Different amount of parameters
38 | if length(node1.Parameters) ~= length(node2.Parameters)
39 | return
40 | end
41 |
42 | if ~isempty(node1.Parameters) && ~isempty(node2.Parameters)
43 | params1 = {node1.Parameters.Name};
44 | params2 = {node2.Parameters.Name};
45 |
46 | % Keep track of what params were checked for node2.
47 | % Nodes may have the same number of params, but different params.
48 | unchecked2 = ones(1, length(params2));
49 |
50 | % Compare for same parameters (but not values)
51 | for i = 1:length(params1)
52 | [member, idx] = ismember(params1(i), params2);
53 | if member
54 | unchecked2(idx) = 0; % Mark as checked
55 | else
56 | return
57 | end
58 | end
59 |
60 | if any(unchecked2) % Some param Names were different, so some where unchecked
61 | return
62 | end
63 | end
64 |
65 | sameNodes = true;
66 | end
--------------------------------------------------------------------------------
/src/plot/editsToDigraph.m:
--------------------------------------------------------------------------------
1 | function D = editsToDigraph(root)
2 | % EDITSTODIGRAPH Create a digraph from a comparison tree.
3 | %
4 | % Inputs:
5 | % root xmlcomp.Edits object.
6 | %
7 | % Outputs:
8 | % D Digraph.
9 |
10 | % Validate inputs
11 | try
12 | assert(isa(root, 'xmlcomp.Edits'))
13 | catch
14 | message = 'Node argument must be an xmlcomp.Edits object.';
15 | error(message)
16 | end
17 |
18 | [source, target, nodes] = createSourceTarget(root);
19 |
20 | D = digraph(source, target);
21 | D.Nodes.Label = nodes;
22 | end
23 |
24 | function [source, target, nodes] = createSourceTarget(root, varargin)
25 | % CREATESOURCETARGET Get the directed graph edges as (source, target) pairs for
26 | % a comparison tree.
27 | % (See www.mathworks.com/help/matlab/ref/digraph.html#mw_26035adf-ff90-4a33-a8f8-42048d7e39a6)
28 | %
29 | % Inputs:
30 | % root xmlcomp.Edits object.
31 | %
32 | % Outputs:
33 | % source Cell array of source nodes.
34 | % target Cell array of target nodes.
35 | % nodes Cell array of node labels.
36 |
37 | % Handle input
38 | omitParam = lower(getInput('OmitParam', varargin, 'on'));
39 |
40 | % Initialize
41 | source = {};
42 | target = {};
43 | nodes = {};
44 |
45 | % Add the root node
46 | nodes{end+1} = 'Edits';
47 |
48 | source{end+1} = 'Edits';
49 | target{end+1} = 'Comparison Root (before)';
50 | nodes{end+1} = 'Comparison Root (before}';
51 |
52 | source{end+1} = 'Edits';
53 | target{end+1} = 'Comparison Root (after)';
54 | nodes{end+1} = 'Comparison Root (after}';
55 |
56 | % Before
57 | nodes_before = getSubTree(root.LeftRoot);
58 |
59 | for j = 1:length(nodes_before)
60 | children = nodes_before(j).Children;
61 | if isempty(children)
62 | continue
63 | else
64 | children = nodes_before(j).Children;
65 | parent = getPathTree(nodes_before(j));
66 | suffix = ' (before)';
67 |
68 | for k = 1:length(children)
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
75 | end
76 | end
77 | end
78 |
79 | % After
80 | nodes_after = getSubTree(root.RightRoot);
81 | for j = 1:length(nodes_after)
82 | children = nodes_after(j).Children;
83 | if isempty(children)
84 | continue
85 | else
86 | children = nodes_after(j).Children;
87 | parent = getPathTree(nodes_after(j));
88 | suffix = ' (after)';
89 |
90 | for k = 1:length(children)
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
97 | end
98 | end
99 | end
100 | nodes = nodes';
101 | end
--------------------------------------------------------------------------------
/src/plot/plotTree.m:
--------------------------------------------------------------------------------
1 | function h = plotTree(root)
2 | % PLOTTREE Plot the digraph of the comparison tree.
3 | %
4 | % Inputs:
5 | % root xmlcomp.Edits object.
6 | %
7 | % Outputs:
8 | % h Handle to the GraphPlot.
9 | % (See www.mathworks.com/help/matlab/ref/graphplot.html)
10 |
11 | G = editsToDigraph(root);
12 |
13 | % Construct plot title
14 | [~, name1, ext1] = fileparts(root.LeftFileName);
15 | [~, name2, ext2] = fileparts(root.RightFileName);
16 | t = ['Comparison tree for ' name1 ext1 ' and ' name2 ext2];
17 |
18 | % Use Simulink-like plot options
19 | % Info on options: https://www.mathworks.com/help/matlab/ref/graph.plot.html
20 | figure
21 | h = plot(G, 'NodeLabel', G.Nodes.Label, 'Layout', 'layered', 'Direction', 'right', 'AssignLayers', 'asap', 'ShowArrows', 'off');
22 |
23 | % Visual improvements
24 | title(t, 'Interpreter' ,'none');
25 | camroll(-90);
26 | set(gca,'xtick',[],'ytick',[])
27 | h.Marker = 's';
28 | h.MarkerSize = 8;
29 |
30 | % Stop underscores from resulting in subscript
31 | try
32 | set(h, 'Interpreter' ,'none');
33 | catch
34 | end
35 | end
--------------------------------------------------------------------------------
/src/printChildren.m:
--------------------------------------------------------------------------------
1 | function printChildren(node)
2 | % PRINTCHILDREN Print to the command window the names of all children.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object for which to print children.
6 | %
7 | % Outputs:
8 | % N/A
9 |
10 | try
11 | assert(isa(node, 'xmlcomp.Node'))
12 | catch
13 | message = 'Node argument must be an xmlcomp.Node object.';
14 | error(message)
15 | end
16 |
17 | for i = 1:length(node.Children)
18 | fprintf('%d %s\n', i, node.Children(i).Name)
19 | end
20 | end
--------------------------------------------------------------------------------
/src/printParams.m:
--------------------------------------------------------------------------------
1 | function printParams(node)
2 | % PRINTPARAMS Print the parameters of a node.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % N/A
9 |
10 | p = node.Parameters;
11 | for i = 1:length(p)
12 | fprintf('%s - ''%s''\n', p(i).Name, p(i).Value);
13 | end
14 | end
--------------------------------------------------------------------------------
/src/summaryOfChanges.m:
--------------------------------------------------------------------------------
1 | function summaryOfChanges(root, printPath, printFile)
2 | % SUMMARYOFCHANGES Print a summary report of all the changes in the comparison tree.
3 | %
4 | % Inputs:
5 | % root xmlcomp.Edits object.
6 | % printPath Whether to print the paths(1) or not(0). [Optional]
7 | % printFile Whether to create a file with the summary(1) or not(0).
8 | % File is created in the current directory, with filename:
9 | % model1_VS_model2.txt. [Optional]
10 | %
11 | % Outputs:
12 | % N/A
13 | %
14 | % Side Effects:
15 | % File output or Command Window output.
16 |
17 | model1 = root.LeftFileName;
18 | model2 = root.RightFileName;
19 | [~, name1, ~] = fileparts(model1);
20 | [~, name2, ~] = fileparts(model2);
21 | if ~bdIsLoaded(name1) || ~bdIsLoaded(name2)
22 | error('Models are not loaded.');
23 | end
24 |
25 | if (nargin < 2) && ~exist('printPath', 'var')
26 | printPath = 0;
27 | end
28 |
29 | % Open file for printing, if applicable
30 | if (nargin > 2) && printFile
31 | % Open the file, then print a header
32 | filename = [name1 '_VS_' name2 '.txt'];
33 | file = fopen(filename, 'wt');
34 | else
35 | filename = '';
36 | file = 1;
37 | end
38 |
39 | % Queries to print
40 | printQuery(root, {'ChangeType', 'added'}, printPath, file);
41 | printQuery(root, {'ChangeType', 'deleted'}, printPath, file);
42 | printQuery(root, {'ChangeType', 'renamed'}, printPath, file);
43 | printQuery(root, {'ChangeType', 'modified'}, printPath, file);
44 | fprintf(file, '\n');
45 |
46 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'added'}, printPath, file);
47 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'deleted'}, printPath, file);
48 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'renamed'}, printPath, file);
49 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'modified'}, printPath, file);
50 | fprintf(file, '\n');
51 |
52 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'added', 'BlockType', 'inport'}, printPath, file);
53 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'deleted', 'BlockType', 'inport'}, printPath, file);
54 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'renamed', 'BlockType', 'inport'}, printPath, file);
55 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'modified', 'BlockType', 'inport'}, printPath, file);
56 | fprintf(file, '\n');
57 |
58 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'added', 'BlockType', 'outport'}, printPath, file);
59 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'deleted', 'BlockType', 'outport'}, printPath, file);
60 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'renamed', 'BlockType', 'outport'}, printPath, file);
61 | printQuery(root, {'NodeType', 'block', 'ChangeType', 'modified', 'BlockType', 'outport'}, printPath, file);
62 | fprintf(file, '\n');
63 |
64 | % printQuery(root, {'NodeType', 'block', 'ChangeType', 'added', 'BlockType', 'subsystem'}, printPath, file);
65 | % printQuery(root, {'NodeType', 'block', 'ChangeType', 'deleted', 'BlockType', 'subsystem'}, printPath, file);
66 | % printQuery(root, {'NodeType', 'block', 'ChangeType', 'renamed', 'BlockType', 'subsystem'}, printPath, file);
67 | % printQuery(root, {'NodeType', 'block', 'ChangeType', 'modified', 'BlockType', 'subsystem'}, printPath, file);
68 | % fprintf(file, '\n');
69 |
70 | printQuery(root, {'NodeType', 'line', 'ChangeType', 'added'}, printPath, file);
71 | printQuery(root, {'NodeType', 'line', 'ChangeType', 'deleted'}, printPath, file);
72 | printQuery(root, {'NodeType', 'line', 'ChangeType', 'renamed'}, printPath, file);
73 | printQuery(root, {'NodeType', 'line', 'ChangeType', 'modified'}, printPath, file);
74 | fprintf(file, '\n');
75 |
76 | % Close file
77 | if ~(file == 1)
78 | fclose(file);
79 | end
80 | end
81 |
82 | function printQuery(root, query, printPath, file)
83 | % PRINTQUERY Perform a query and print.
84 | %
85 | % Inputs:
86 | % root xmlcomp.Edits object.
87 | % query Cell array of find_node constriant pairs.
88 | % printPath Whether to print the paths(1) or not(0). [Optional]
89 | % file Filename or 1 for output to Command Window.
90 | %
91 | % Outputs:
92 | % N/A
93 | %
94 | % Side Effects:
95 | % File output or Command Window output.
96 |
97 | % Get data
98 | [~,p] = find_node(root, query{:});
99 | n = length(p);
100 |
101 | % Construct formatting for query
102 | format = repmat('%s, ', 1, length(query));
103 | format = format(1:end-2); % Take off last comma and space
104 |
105 | % Print query
106 | fprintf(file, format, query{:});
107 | fprintf(file, ' -- TOTAL %d\n', n);
108 |
109 | % Print paths
110 | if printPath
111 | printPaths(p, file);
112 | end
113 | end
114 |
115 | function printPaths(paths, file)
116 | % PRINTPATHS Print a cell array of paths.
117 | %
118 | % Inputs:
119 | % paths Cell array of paths char arrays.
120 | % file Filename or 1 for output to Command Window.
121 | %
122 | % Outputs:
123 | % N/A
124 | %
125 | % Side Effects:
126 | % File output or Command Window output.
127 |
128 | for i = 1:length(paths)
129 | line = strrep(cell2mat(paths(i)), newline, ' ');
130 | fprintf(file, '\t%s\n', line);
131 | end
132 | end
--------------------------------------------------------------------------------
/src/treeDepth.m:
--------------------------------------------------------------------------------
1 | function d = treeDepth(node)
2 | % TREEDEPTH Get the depth of the comparison tree.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object at which to start the depth finding.
6 | %
7 | % Outputs:
8 | % d Depth.
9 |
10 | d = 0;
11 | if ~hasChildren(node)
12 | return;
13 | end
14 |
15 | for i = 1:length(node.Children)
16 | d = max(d, treeDepth(node.Children(i)));
17 | end
18 |
19 | if ~strcmp(node.Name, 'Comparison Root')
20 | d = d + 1;
21 | end
22 | end
--------------------------------------------------------------------------------
/src/treeNumNodes.m:
--------------------------------------------------------------------------------
1 | function n = treeNumNodes(node)
2 | % TREENUMNODES Get the number of nodes in the comparison tree. This includes
3 | % all nodes (e.g. Comparison Root, etc.) but not xmlcomp.Edits.
4 | %
5 | % Inputs:
6 | % node xmlcomp.Edits or xmlcomp.Node object.
7 | %
8 | % Outputs:
9 | % n Number of nodes.
10 |
11 | if isa(node, 'xmlcomp.Node')
12 | n = 1;
13 | if ~hasChildren(node)
14 | return
15 | end
16 |
17 | for i = 1:numel(node.Children)
18 | n = n + treeNumNodes(node.Children(i));
19 | end
20 | elseif isa(node, 'xmlcomp.Edits')
21 | n = 0;
22 | n = n + treeNumNodes(node.LeftRoot);
23 | n = n + treeNumNodes(node.RightRoot);
24 | else
25 | message = 'Node argument must be an xmlcomp.Edits or xmlcomp.Node object.';
26 | error(message)
27 | end
28 | end
--------------------------------------------------------------------------------
/src/treeToTable.m:
--------------------------------------------------------------------------------
1 | function t = treeToTable(root)
2 | % TREETOTABLE Produce a table of nodes from the comparison tree.
3 | %
4 | % Inputs:
5 | % root xmlcomp.Edits object.
6 | %
7 | % Outputs:
8 | % t Table.
9 | %
10 | % Example:
11 | % >> Edits = slxmlcomp.compare('demo_after', 'demo_before');
12 | % >> t = classifyChanges(Edits)
13 | %t =
14 | %
15 | % 13x5 table
16 | %
17 | % nodes path changeType nodeType blockType
18 | % __________________ ________________________________________________ ____________ _________ ___________________
19 | %
20 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Data Store↵Memory' } {'deleted' } {'block'} {'DataStoreMemory'}
21 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Add' } {'modified'} {'block'} {'Sum' }
22 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Constant' } {'modified'} {'block'} {'Constant' }
23 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Gain' } {'deleted' } {'block'} {'Gain' }
24 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/NewName' } {'renamed' } {'block'} {'Outport' }
25 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/In2/1' } {'deleted' } {'line' } {0x0 char }
26 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Gain/1' } {'deleted' } {'line' } {0x0 char }
27 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Add' } {'modified'} {'block'} {'Sum' }
28 | % [1x1 xmlcomp.Node] {'demo_after/Subsystem/Subsystem/Constant' } {'modified'} {'block'} {'Constant' }
29 | % [1x1 xmlcomp.Node] {'demo_before/Subsystem/Subsystem/Out1' } {'renamed' } {'block'} {'Outport' }
30 | % [1x1 xmlcomp.Node] {'demo_before/Subsystem/Subsystem/Integrator' } {'added' } {'block'} {'Integrator' }
31 | % [1x1 xmlcomp.Node] {'demo_before/Subsystem/Subsystem/In2/1' } {'added' } {'line' } {0x0 char }
32 | % [1x1 xmlcomp.Node] {'demo_before/Subsystem/Subsystem/Integrator/1'} {'added' } {'line' } {0x0 char }
33 |
34 | % Validate inputs
35 | try
36 | assert(isa(root, 'xmlcomp.Edits'))
37 | catch
38 | message = 'Node argument must be an xmlcomp.Edits object.';
39 | error(message)
40 | end
41 |
42 | % Find all nodes that are actual changes
43 | nodes = find_node(root, 'ChangeType', {'added', 'deleted', 'modified', 'renamed'});
44 |
45 | % Initialize outputs
46 | changeType = cell(size(nodes));
47 | path = cell(size(nodes));
48 | nodeType = cell(size(nodes));
49 | blockType = cell(size(nodes));
50 |
51 | % For each change
52 | for i = 1:length(nodes)
53 | changeType{i} = getNodeChangeType(nodes(i), root);
54 |
55 | % Get handle in model
56 | hdl = getHandle(nodes(i), root.LeftFileName);
57 | if isempty(hdl)
58 | hdl = getHandle(nodes(i), root.RightFileName);
59 | end
60 |
61 | % Get path in model
62 | path{i} = getfullname(hdl);
63 | if isempty(path{i})
64 | path{i} = '';
65 | end
66 |
67 | % Get node type
68 | nodeType{i} = get_param(hdl, 'Type');
69 | if isempty(nodeType{i})
70 | nodeType{i} = getNodeType(nodes(i), root.LeftFileName);
71 | end
72 | if isempty(nodeType{i})
73 | nodeType{i} = getNodeType(nodes(i), root.RightFileName);
74 | end
75 | if isempty(nodeType(i))
76 | nodeType{i} = '';
77 | end
78 |
79 | % Get block type
80 | if strcmp(nodeType{i}, 'block')
81 | blockType{i} = get_param(hdl, 'BlockType');
82 | else
83 | blockType{i} = '';
84 | end
85 | end
86 |
87 | t = table(nodes, path, changeType, nodeType, blockType);
88 | end
--------------------------------------------------------------------------------
/src/type/blocktype/getNodeBlockType.m:
--------------------------------------------------------------------------------
1 | function type = getNodeBlockType(node, sys)
2 | % GETNODEBLOCKTYPE Determine the block type that the node represents in the model.
3 | % In general, only elements which are Edited can have their type inferred
4 | % directly from the tree. For those that cannot be inferred, if sys is
5 | % provided, then the model will be checked.
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | % sys Path or name of the model. [Optional]
10 | %
11 | % Outputs:
12 | % type The BlockType of the node.
13 |
14 | % Validate inputs
15 | try
16 | assert(isa(node, 'xmlcomp.Node'))
17 | catch
18 | message = 'Node argument must be an xmlcomp.Node object.';
19 | error(message)
20 | end
21 |
22 | type = 'unknown';
23 |
24 | if isBlock(node)
25 | % Try checking parameters
26 | hasBlockType = false;
27 | if ~isempty(node.Parameters)
28 | hasBlockType = any(strcmp({node.Parameters.Name}, 'BlockType'));
29 | end
30 |
31 | if hasBlockType
32 | idx = strcmp({node.Parameters.Name}, 'BlockType');
33 | idx = find(idx);
34 | values = {node.Parameters.Value};
35 | type = char(values(idx));
36 | end
37 |
38 | % If still unknown and sys is provided, check model
39 | if strcmp(type, 'unknown') && exist('sys', 'var')
40 | path = getPath(node, sys);
41 | try
42 | type = get_param(path, 'BlockType');
43 | catch
44 | error('Trying to find the block type for a non-block. Look into why this was not caught by the isBlock function.')
45 | end
46 | end
47 | end
48 | end
--------------------------------------------------------------------------------
/src/type/changetype/getNodeChangeType.m:
--------------------------------------------------------------------------------
1 | function type = getNodeChangeType(node, root)
2 | % GETNODECHANGETYPE Determine the type of change that the node represents in
3 | % the tree: added, deleted, renamed, modified, or none. For more information
4 | % on what each of these mean, see the isAdded, isDeleted, isRenamed, or
5 | % isModified functions.
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | % root xmlcomp.Edits object.
10 | %
11 | % Outputs:
12 | % type The type of change.
13 |
14 | % Validate inputs
15 | try
16 | assert(isa(node, 'xmlcomp.Node'))
17 | catch
18 | message = 'Node argument must be an xmlcomp.Node object.';
19 | error(message)
20 | end
21 |
22 | try
23 | assert(isa(root, 'xmlcomp.Edits'))
24 | catch
25 | message = 'Root argument must be an xmlcomp.Edits object.';
26 | error(message)
27 | end
28 |
29 | if isRenamed(node) && ~isModified(node)
30 | type = 'renamed';
31 | elseif isRenamed(node) && isModified(node)
32 | type = {'renamed', 'modified'};
33 | elseif isModified(node)
34 | type = 'modified';
35 | elseif isAdded(node, root)
36 | type = 'added';
37 | elseif isDeleted(node, root)
38 | type = 'deleted';
39 | else
40 | type = 'none';
41 | end
42 | end
--------------------------------------------------------------------------------
/src/type/changetype/isAdded.m:
--------------------------------------------------------------------------------
1 | function added = isAdded(node, root)
2 | % ISADDED Determine if the node has been added. An added node is one that
3 | % is in the right branch, without a corresponding element in the left branch.
4 | %
5 | % Inputs:
6 | % node xmlcomp.Node object.
7 | % root xmlcomp.Edits object.
8 | %
9 | % Outputs:
10 | % added Whether the node has been added(1) or not(0).
11 |
12 | % Validate inputs
13 | try
14 | assert(isa(node, 'xmlcomp.Node'))
15 | catch
16 | message = 'Node argument must be an xmlcomp.Node object.';
17 | error(message)
18 | end
19 |
20 | try
21 | assert(isa(root, 'xmlcomp.Edits'))
22 | catch
23 | message = 'Root argument must be an xmlcomp.Edits object.';
24 | error(message)
25 | end
26 |
27 | % Note: Added nodes are are flagged as Edited, but so are deleted
28 | % nodes, so this is not a reliable way of determining if the node is added.
29 |
30 | added = false;
31 |
32 | if ~isempty(node.Partner)
33 | return
34 | end
35 |
36 | % If in the right branch only
37 | if (getBranch(node, root) == 1)
38 | added = true;
39 | end
40 | end
--------------------------------------------------------------------------------
/src/type/changetype/isDeleted.m:
--------------------------------------------------------------------------------
1 | function deleted = isDeleted(node, root)
2 | % ISDELETED Determine if the node has been deleted. A removed node is one that
3 | % is in the left branch, without a corresponding element in the right branch.
4 | %
5 | % Inputs:
6 | % node xmlcomp.Node object.
7 | % root xmlcomp.Edits object.
8 | %
9 | % Outputs:
10 | % deleted Whether the node has been deleted(1) or not(0).
11 |
12 | % Validate inputs
13 | try
14 | assert(isa(node, 'xmlcomp.Node'))
15 | catch
16 | message = 'Node argument must be an xmlcomp.Node object.';
17 | error(message)
18 | end
19 |
20 | try
21 | assert(isa(root, 'xmlcomp.Edits'))
22 | catch
23 | message = 'Root argument must be an xmlcomp.Edits object.';
24 | error(message)
25 | end
26 |
27 | % Note: Deleted nodes are are flagged as Edited, but so are added
28 | % nodes, so this is not a reliable way of determining if the node is added.
29 |
30 | deleted = false;
31 |
32 | if ~isempty(node.Partner)
33 | return
34 | end
35 |
36 | % If in the left branch only
37 | if (getBranch(node, root) == 0)
38 | deleted = true;
39 | end
40 | end
--------------------------------------------------------------------------------
/src/type/changetype/isModified.m:
--------------------------------------------------------------------------------
1 | function modified = isModified(node, checkPorts)
2 | % ISMODIFIED Determine if the node has been modified. A modified node is
3 | % one with a corresponding element in the model against which it is being
4 | % compared, and has different parameters and/or different parents.
5 | %
6 | % Modification does not mean added/removed/renamed. To check for this,
7 | % see isAdded, isRemoved, or isRenamed functions.
8 | %
9 | % Inputs:
10 | % node xmlcomp.Node object.
11 | % checkPorts Whether to consider changes in number of subsystem ports(1) or not(0).
12 | %
13 | % Outputs:
14 | % modified Whether the node has been modified(1) or not(0).
15 |
16 | % Validate inputs
17 | try
18 | assert(isa(node, 'xmlcomp.Node'))
19 | catch
20 | message = 'Node argument must be an xmlcomp.Node object.';
21 | error(message)
22 | end
23 |
24 | if nargin < 2
25 | checkPorts = true;
26 | end
27 |
28 | % Note: Modified nodes are not necessarily flagged as Edited.
29 |
30 | modified = false;
31 |
32 | % Has a correspodinging node
33 | if isempty(node.Partner)
34 | return
35 | else
36 | % Check if parents are different
37 | if isempty(node.Parent) && isempty(node.Partner.Parent)
38 | % Root nodes don't have parents
39 | elseif xor(isempty(node.Parent), isempty(node.Partner.Parent)) || ...
40 | ~nodecmp(node.Parent, node.Partner.Parent)
41 | % One node was a root, while the other is not anymore, or
42 | % the parents are different nodes.
43 | % This occurs when a model element is moved.
44 | modified = true;
45 | return
46 | end
47 | end
48 |
49 | % Both have parameters to check
50 | if ~isempty(node.Parameters) && ~isempty(node.Partner.Parameters)
51 | params1 = {node.Parameters.Name};
52 | params2 = {node.Partner.Parameters.Name};
53 | values1 = {node.Parameters.Value};
54 | values2 = {node.Partner.Parameters.Value};
55 |
56 | % Keep track of what params were checked.
57 | % Nodes may have same number of params, but different params
58 | unchecked2 = ones(1, length(params2));
59 |
60 | % Compare Values for matching Names
61 | for i = 1:length(params1)
62 | [member, idx] = ismember(params1(i), params2);
63 | if member == 0
64 | % Parameter doesn't exist in the other nodes parameter list
65 | continue
66 | end
67 | if strcmp(params1(i), 'Name') && strcmp(params2(idx), 'Name') % Skip differences in Name param. This is classified as 'renamed' type.
68 | unchecked2(idx) = 0; % Mark as checked
69 | elseif ~checkPorts && (strcmp(params1(i), 'Ports') && strcmp(params2(idx), 'Ports')) % Skip differences in Ports param
70 | unchecked2(idx) = 0; % Mark as checked
71 | elseif member && strcmp(values1(i), values2(idx))
72 | unchecked2(idx) = 0; % Mark as checked
73 | else
74 | modified = true;
75 | return
76 | end
77 | end
78 |
79 | if any(unchecked2) % Some param Names were different, so some where unchecked
80 | modified = true;
81 | return
82 | end
83 | end
84 | end
--------------------------------------------------------------------------------
/src/type/changetype/isRenamed.m:
--------------------------------------------------------------------------------
1 | function out = isRenamed(node)
2 | % ISRENAMED Determine if the node has been renamed. A renamed node is one
3 | % with a corresponding element in the model against which it is being
4 | % compared, but does not have the same name.
5 | %
6 | % Inputs:
7 | % node xmlcomp.Node object.
8 | %
9 | % Outputs:
10 | % out Whether the node has been renamed(1) or not(0).
11 |
12 | % Validate inputs
13 | try
14 | assert(isa(node, 'xmlcomp.Node'))
15 | catch
16 | message = 'Node argument must be an xmlcomp.Node object.';
17 | error(message)
18 | end
19 |
20 | % Note: Renamed nodes are not necessarily flagged as Edited. They are
21 | % treated as two different nodes.
22 |
23 | out = false;
24 | if ~isempty(node.Partner) && ~strcmp(node.Name, node.Partner.Name)
25 | out = true;
26 | end
27 | end
--------------------------------------------------------------------------------
/src/type/isInport.m:
--------------------------------------------------------------------------------
1 | function out = isInport(node)
2 | % ISINPORT Determine if the node represents an inport block.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % out Whether the node represents an inport block(1) or not(0).
9 |
10 | % Validate inputs
11 | try
12 | assert(isa(node, 'xmlcomp.Node'))
13 | catch
14 | message = 'Node argument must be an xmlcomp.Node object.';
15 | error(message)
16 | end
17 |
18 | % Nodes representing blocks have their name set to the Name parameter of
19 | % the block, so we have to do some deduction
20 |
21 | hasBlockType = false;
22 | if ~isempty(node.Parameters)
23 | hasBlockType = any(strcmp({node.Parameters.Name}, 'BlockType'));
24 | end
25 |
26 | isIn = false;
27 | if hasBlockType
28 | idx = strcmp({node.Parameters.Name}, 'BlockType');
29 | idx = find(idx);
30 | values = {node.Parameters.Value};
31 |
32 | isIn = strcmp(char(values(idx)), 'Inport');
33 | end
34 |
35 | out = isIn && ~(isLine(node) || isPort(node) || isMask(node) || isAnnotation(node));
36 | end
--------------------------------------------------------------------------------
/src/type/isOutport.m:
--------------------------------------------------------------------------------
1 | function out = isOutport(node)
2 | % ISOUTPORT Determine if the node represents an outport block.
3 | %
4 | % Inputs:
5 | % node xmlcomp.Node object.
6 | %
7 | % Outputs:
8 | % out Whether the node represents an outport block(1) or not(0).
9 |
10 | % Validate inputs
11 | try
12 | assert(isa(node, 'xmlcomp.Node'))
13 | catch
14 | message = 'Node argument must be an xmlcomp.Node object.';
15 | error(message)
16 | end
17 |
18 | % Nodes representing blocks have their name set to the Name parameter of
19 | % the block, so we have to do some deduction
20 |
21 | hasBlockType = false;
22 | if ~isempty(node.Parameters)
23 | hasBlockType = any(strcmp({node.Parameters.Name}, 'BlockType'));
24 | end
25 |
26 | isOut = false;
27 | if hasBlockType
28 | idx = strcmp({node.Parameters.Name}, 'BlockType');
29 | idx = find(idx);
30 | values = {node.Parameters.Value};
31 |
32 | isOut = strcmp(char(values(idx)), 'Outport');
33 | end
34 |
35 | out = isOut && ~(isLine(node) || isPort(node) || isMask(node) || isAnnotation(node));
36 | end
--------------------------------------------------------------------------------
/src/type/isStateflow.m:
--------------------------------------------------------------------------------
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/isSubsystem.m:
--------------------------------------------------------------------------------
1 | function out = isSubsystem(node)
2 | % ISSUBSYSTEM Determine if the node represents a Subsystem block.
3 | %
4 | % Caution: xmlcomp.Node objects representing Subsystems in the model will
5 | % be in the xmlcomp.Edits tree for preserving the hierarchy, even
6 | % though the subsystem block itself is not edited (i.e. node.Edited is 0).
7 | % If the Subsystem is unedited, it will not be detectable as a Subsystem
8 | % because it will have empty parameters. Therefore, this function can
9 | % only detect Edited subsystems. All others are ignored.
10 | %
11 | % Inputs:
12 | % node xmlcomp.Node object.
13 | %
14 | % Outputs:
15 | % out Whether the node represents a Subsystem block(1) or not(0).
16 |
17 | try
18 | assert(isa(node, 'xmlcomp.Node'))
19 | catch
20 | message = 'Node argument must be an xmlcomp.Node object.';
21 | error(message)
22 | end
23 |
24 | % Nodes representing blocks have their name set to the Name parameter of the
25 | % block, so we have to do some deduction
26 |
27 | hasBlockType = false;
28 | if ~isempty(node.Parameters)
29 | hasBlockType = any(strcmp({node.Parameters.Name}, 'BlockType'));
30 | end
31 |
32 | isSubSys = false;
33 | if hasBlockType
34 | idx = strcmp({node.Parameters.Name}, 'BlockType');
35 | idx = find(idx);
36 | values = {node.Parameters.Value};
37 |
38 | isSubSys = strcmp(char(values(idx)), 'SubSystem');
39 | end
40 |
41 | out = isSubSys && ~(isLine(node) || isPort(node) || isMask(node) || isAnnotation(node));
42 | end
--------------------------------------------------------------------------------
/src/type/nodetype/getNodeType.m:
--------------------------------------------------------------------------------
1 | function type = getNodeType(node, varargin)
2 | % GETNODETYPE Determine the type of element that the node represents in the
3 | % model: block, block_diagram, line, port, mask, annotation, or stateflow.
4 | %
5 | % In general, only elements which are Edited can have their type inferred
6 | % directly from the tree. For those that cannot be inferred, if sys is
7 | % provided, then the model will be checked. Otherwise, it is unknown.
8 | %
9 | % Inputs:
10 | % node xmlcomp.Node object.
11 | % sys Path or name of the model. [Optional]
12 | %
13 | % Outputs:
14 | % type The type of node.
15 |
16 | % Validate inputs
17 | try
18 | assert(isa(node, 'xmlcomp.Node'))
19 | catch
20 | message = 'Node argument must be an xmlcomp.Node object.';
21 | error(message)
22 | end
23 |
24 | narginchk(1,2);
25 |
26 | sys = [];
27 | if nargin > 2
28 | error('Too many input arguments');
29 | elseif nargin == 2
30 | sys = varargin{:};
31 | end
32 |
33 | if ~isempty(sys)
34 | % Get the model name
35 | [~,sys,~] = fileparts(sys);
36 |
37 | % Check if loaded
38 | sysLoaded = bdIsLoaded(sys);
39 | if ~sysLoaded
40 | message = ['Model ' sys ' is not loaded.' newline];
41 | error(message)
42 | end
43 | end
44 |
45 | % First try to see if we can check the type without looking at the model.
46 | % If that does not work, check the model
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'))
52 | type = 'block';
53 | elseif isBlockDiagram(node)
54 | type = 'block_diagram';
55 | elseif isLine(node)
56 | type = 'line';
57 | elseif isPort(node)
58 | type = 'port';
59 | elseif isMask(node)
60 | type = 'mask';
61 | elseif isConfiguration(node)
62 | type = 'configuration';
63 | elseif isAnnotation(node)
64 | type = 'annotation';
65 | elseif ~isempty(sys)
66 | path = getPath(node, varargin);
67 | if ~isempty(path) % Tree artifacts don't have paths (e.g. Comparison Root)
68 | try
69 | type = get_param(path, 'Type');
70 | catch
71 | % The path was not valid
72 | type = 'unknown';
73 | end
74 | else
75 | type = 'unknown';
76 | end
77 | else
78 | type = 'unknown';
79 | end
80 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isAnnotation.m:
--------------------------------------------------------------------------------
1 | function out = isAnnotation(node)
2 | % ISANNOTATION Determine if the node represents an annotation, based on the tree
3 | % only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents an annotation(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Can be identified if it has the InternalMargins parameter
22 | if ~isempty(node.Parameters) && any(strcmp({node.Parameters.Name}, 'InternalMargins'))
23 | out = true;
24 | else
25 | out = false;
26 | end
27 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isBlock.m:
--------------------------------------------------------------------------------
1 | function out = isBlock(node)
2 | % ISBLOCK Determine if the node represents a block, based on the tree
3 | % only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents a block(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Nodes representing blocks have their name set to the Name parameter of
22 | % the block, so we have to do some deduction
23 |
24 | % If it has a BlockType parameter, then it's a block.
25 | % Note: Only useful if the node is Edited. If it is not edited, the node will
26 | % not have a BlockType parameter. This is an issue for Subsystems that are
27 | % included for maintaining the hierarchy.
28 | hasBlockType = false;
29 | if ~isempty(node.Parameters)
30 | hasBlockType = any(strcmp({node.Parameters.Name}, 'BlockType'));
31 | end
32 |
33 | isOther = (isLine(node) || isPort(node) || isMask(node) || isAnnotation(node) || isBlockDiagram(node) || isConfiguration(node));
34 | out = hasBlockType || ~(isOther);
35 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isBlockDiagram.m:
--------------------------------------------------------------------------------
1 | function out = isBlockDiagram(node)
2 | % ISBLOCKDIAGRAM Determine if the node presents a block_diagram, based on the
3 | % tree only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents a block_diagram(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | if strcmp(node.Name, 'Simulink') && any(strcmp(node.Parent.Name, {'Comparison Root', 'SLX Comparison Root'}))
22 | out = true;
23 | else
24 | out = false;
25 | end
26 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isConfiguration.m:
--------------------------------------------------------------------------------
1 | function out = isConfiguration(node)
2 | % ISCONFIGURATION Determine if the node represents a model configuration parameter,
3 | % based on the tree only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents a configuration(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Can be identified if any ancestor is
22 | path = getPathTree(node);
23 | parent = strfind(path, '/Model Configuration Sets/');
24 | if ~isempty(parent)
25 | out = true;
26 | else
27 | out = false;
28 | end
29 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isLine.m:
--------------------------------------------------------------------------------
1 | function out = isLine(node)
2 | % ISLINE Determine if the node represents a line, based on the tree
3 | % only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % l Whether the node represents a line(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Can be identified simply by its name
22 | out = contains(node.Name, '->');
23 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isMask.m:
--------------------------------------------------------------------------------
1 | function out = isMask(node)
2 | % ISMASK Determine if the node represents a mask, based on the tree
3 | % only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents a mask(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Can be identified simply by its name
22 | out = strcmp(node.Name, 'Mask');
23 | end
--------------------------------------------------------------------------------
/src/type/nodetype/isPort.m:
--------------------------------------------------------------------------------
1 | function out = isPort(node)
2 | % ISPORT Determine if the node represents a port to a block, based on the tree
3 | % only (i.e. does not check the model).
4 | %
5 | % See: getNodeType.m
6 | %
7 | % Inputs:
8 | % node xmlcomp.Node object.
9 | %
10 | % Outputs:
11 | % out Whether the node represents a port(1) or not(0).
12 |
13 | % Validate inputs
14 | try
15 | assert(isa(node, 'xmlcomp.Node'))
16 | catch
17 | message = 'Node argument must be an xmlcomp.Node object.';
18 | error(message)
19 | end
20 |
21 | % Can be identified simply by its name if the user does not specify one
22 | hasPortName = strcmp(node.Name, 'Port');
23 |
24 | % Named signals result in ports that don't have the generic Port name, but they have a port number
25 |
26 | try
27 | hasPortNumber = any(strcmp({node.Parameters.Name}, 'PortNumber'));
28 | catch ME
29 | if strcmp(ME.identifier, 'MATLAB:structRefFromNonStruct')
30 | hasPortNumber = false;
31 | else
32 | rethrow(ME);
33 | end
34 | end
35 |
36 | out = hasPortName || hasPortNumber;
37 | end
--------------------------------------------------------------------------------