├── .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 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.4321649.svg)](https://doi.org/10.5281/zenodo.4321649) 3 | [![View Model Comparison Utility on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](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 --------------------------------------------------------------------------------