├── docs
├── .nojekyll
├── extend.md
├── api.md
├── sw.js
├── images
│ ├── logo
│ │ ├── Image2.psp
│ │ ├── Image3.psp
│ │ └── generate_logo.m
│ ├── matsim-icon.png
│ ├── matsim-logo.png
│ ├── readme
│ │ ├── readme_1.PNG
│ │ ├── readme_2.PNG
│ │ ├── readme_3.PNG
│ │ ├── readme_4.PNG
│ │ ├── readme_5.PNG
│ │ └── readme_6.PNG
│ └── quickstart
│ │ ├── connect_1.PNG
│ │ ├── connect_2.PNG
│ │ └── connect_3.PNG
├── sidebar.md
├── index.html
├── README.md
└── quickstart.md
├── .gitignore
├── merge.bat
├── tests
├── test_sources.m
├── test_sinks.m
├── test_dynamics.m
├── test_operations.m
├── test_subsystems.m
├── test_base.m
├── test_flow.m
└── test_run.m
├── +matsim
├── +helpers
│ ├── isArgSpecified.m
│ ├── unpack.m
│ ├── validateArgs.m
│ ├── getBlockPath.m
│ ├── validateInputs.m
│ ├── getValidParent.m
│ └── findBlock.m
├── +utils
│ ├── handlevar.m
│ ├── getversion.m
│ ├── getBlockPorts.m
│ ├── cell2str.m
│ └── quicksort.m
├── +library
│ ├── Abs.m
│ ├── Sign.m
│ ├── Cos.m
│ ├── Exp.m
│ ├── Sin.m
│ ├── Tan.m
│ ├── Atan.m
│ ├── Terminator.m
│ ├── Max.m
│ ├── Min.m
│ ├── Spacer.m
│ ├── Gain.m
│ ├── Demux.m
│ ├── ToWorkspace.m
│ ├── simOutput.m
│ ├── EnabledSubsystem.m
│ ├── block_input.m
│ ├── SwitchCase.m
│ ├── FromWorkspace.m
│ ├── Lookup1D.m
│ ├── Selector.m
│ ├── Constant.m
│ ├── Integrator.m
│ ├── BusSelector.m
│ ├── unary_operator.m
│ ├── Add.m
│ ├── Merge.m
│ ├── Mux.m
│ ├── Delay.m
│ ├── binary_operator.m
│ ├── Switch.m
│ ├── Lookup2D.m
│ ├── BusCreator.m
│ ├── MultiPortSwitch.m
│ ├── REF.m
│ ├── Scope.m
│ ├── IF.m
│ ├── MatlabFunction.m
│ ├── simulation.m
│ ├── Subsystem.m
│ └── block.m
└── +builder
│ ├── +common
│ ├── isRangeOverlap.m
│ ├── emptyRect.m
│ └── detectOverlaps.m
│ └── +graphviz
│ ├── sim2adj.m
│ ├── getNeighbours.m
│ ├── genLayout.m
│ └── simlayout.m
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── push.bat
├── tools
├── msim_get_port.m
├── msim_layout.m
├── msim_createsubsystem.m
├── msim_align.m
├── fun2model.m
├── msim_add_block.m
└── msim_add_line.m
├── generator
├── source.txt
├── unary.txt
├── binary.txt
├── multiple.txt
├── ReadTable.m
├── Wizard.m
└── GenerateClass.m
├── LICENSE
├── example_1.m
└── README.md
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/extend.md:
--------------------------------------------------------------------------------
1 | # Extending the library
--------------------------------------------------------------------------------
/docs/api.md:
--------------------------------------------------------------------------------
1 | # API Guide
2 |
3 | ## Supported blocks
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | slprj/
2 | *.mat
3 | *.asv
4 | *.autosave
5 |
--------------------------------------------------------------------------------
/docs/sw.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/sw.js
--------------------------------------------------------------------------------
/docs/images/logo/Image2.psp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/logo/Image2.psp
--------------------------------------------------------------------------------
/docs/images/logo/Image3.psp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/logo/Image3.psp
--------------------------------------------------------------------------------
/docs/images/matsim-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/matsim-icon.png
--------------------------------------------------------------------------------
/docs/images/matsim-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/matsim-logo.png
--------------------------------------------------------------------------------
/docs/images/readme/readme_1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_1.PNG
--------------------------------------------------------------------------------
/docs/images/readme/readme_2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_2.PNG
--------------------------------------------------------------------------------
/docs/images/readme/readme_3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_3.PNG
--------------------------------------------------------------------------------
/docs/images/readme/readme_4.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_4.PNG
--------------------------------------------------------------------------------
/docs/images/readme/readme_5.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_5.PNG
--------------------------------------------------------------------------------
/docs/images/readme/readme_6.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/readme/readme_6.PNG
--------------------------------------------------------------------------------
/merge.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | git checkout master
4 | git merge develop
5 | git push
6 | git checkout develop
7 |
--------------------------------------------------------------------------------
/docs/images/quickstart/connect_1.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/quickstart/connect_1.PNG
--------------------------------------------------------------------------------
/docs/images/quickstart/connect_2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/quickstart/connect_2.PNG
--------------------------------------------------------------------------------
/docs/images/quickstart/connect_3.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gave92/Matsim/HEAD/docs/images/quickstart/connect_3.PNG
--------------------------------------------------------------------------------
/tests/test_sources.m:
--------------------------------------------------------------------------------
1 | function [] = test_sources(sys)
2 | %TEST_SOURCES
3 |
4 | import matsim.library.*
5 |
6 | c = Constant('TEST');
7 | w = FromWorkspace('NAME');
8 |
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/+matsim/+helpers/isArgSpecified.m:
--------------------------------------------------------------------------------
1 | function s = isArgSpecified(p,arg)
2 | %ISARGSPECIFIED User specified an argument
3 |
4 | s = ~any(strcmp(p.UsingDefaults,arg)) && (iscell(p.Results.(arg)) || ~isempty(p.Results.(arg)));
5 |
6 | end
7 |
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve Matsim
4 |
5 | ---
6 |
7 | * Operating system (Windows, macOS, Linux)
8 | * MATLAB version
9 | * Script causing the issue
10 |
--------------------------------------------------------------------------------
/docs/sidebar.md:
--------------------------------------------------------------------------------
1 | * [Intro](/)
2 | * [Getting started](quickstart.md)
3 | * [Installation](quickstart.md#installation)
4 | * [Concepts](quickstart.md#concepts)
5 | * [Examples](quickstart.md#examples)
6 | * [API](api.md)
7 | * [Extending](extend.md)
8 |
--------------------------------------------------------------------------------
/+matsim/+utils/handlevar.m:
--------------------------------------------------------------------------------
1 | classdef handlevar < handle
2 | properties
3 | Value
4 | end
5 |
6 | methods
7 | function this = handlevar(value)
8 | this.Value = value;
9 | end
10 | end
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/tests/test_sinks.m:
--------------------------------------------------------------------------------
1 | function [] = test_base(sys)
2 | %TEST_BASE
3 |
4 | import matsim.library.*
5 |
6 | in = Constant(0);
7 |
8 | s = Scope({[{},in],0});
9 | t = Terminator();
10 | w = ToWorkspace(in,'VariableName','TEST_NAME');
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/+matsim/+library/Abs.m:
--------------------------------------------------------------------------------
1 | function blk = Abs(varargin)
2 | %ABS Creates a simulink Abs block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Abs(input,'Name','myAbs');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Abs');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Sign.m:
--------------------------------------------------------------------------------
1 | function blk = Sign(varargin)
2 | %SIGN Creates a simulink Sign block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Sign(input,'Name','mySign');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Sign');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Cos.m:
--------------------------------------------------------------------------------
1 | function blk = Cos(varargin)
2 | %COS Creates a simulink Cos block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Cos(input,'Name','myCos');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Trigonometric Function','Operator','Cos');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Exp.m:
--------------------------------------------------------------------------------
1 | function blk = Exp(varargin)
2 | %Exp Creates a simulink exponential block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Exp(input,'Name','myExp');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Math Function','Operator','exp');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Sin.m:
--------------------------------------------------------------------------------
1 | function blk = Sin(varargin)
2 | %SIN Creates a simulink Sin block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Sin(input,'Name','mySin');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Trigonometric Function','Operator','Sin');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Tan.m:
--------------------------------------------------------------------------------
1 | function blk = Tan(varargin)
2 | %TAN Creates a simulink Tan block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Tan(input,'Name','myTan');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Trigonometric Function','Operator','Tan');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Atan.m:
--------------------------------------------------------------------------------
1 | function blk = Atan(varargin)
2 | %ATAN Creates a simulink Atan block.
3 | % Example:
4 | % input = Constant('var1');
5 | % blk = Atan(input,'Name','myAtan');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Trigonometric Function','Operator','Atan');
10 | end
11 |
--------------------------------------------------------------------------------
/+matsim/+library/Terminator.m:
--------------------------------------------------------------------------------
1 | function blk = Terminator(varargin)
2 | %TERMINATOR Creates a simulink Terminator block.
3 | % Example:
4 | % input = Demux();
5 | % blk = Terminator(input.outport(2),'Name','myTerminator');
6 | %
7 | % See also UNARY_OPERATOR.
8 |
9 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Terminator');
10 | end
11 |
--------------------------------------------------------------------------------
/tests/test_dynamics.m:
--------------------------------------------------------------------------------
1 | function [] = test_dynamics(sys)
2 | %TEST_DYNAMICS
3 |
4 | import matsim.library.*
5 |
6 | in = Constant(0);
7 |
8 | Delay({},'SampleTime',0.1);
9 | Delay(in,'DelayLength',2);
10 | Delay('x0',1);
11 |
12 | Integrator();
13 | Integrator(in,'SampleTime',0.1);
14 | Integrator(in);
15 |
16 | end
17 |
18 |
--------------------------------------------------------------------------------
/+matsim/+library/Max.m:
--------------------------------------------------------------------------------
1 | function blk = Max(varargin)
2 | %MAX Creates a simulink Max block.
3 | % Example:
4 | % in1 = Constant('var1');
5 | % in2 = Constant('var2');
6 | % blk = Max(in1,in2,'name','myMax');
7 | %
8 | % See also BINARY_OPERATOR.
9 |
10 | blk = matsim.library.binary_operator(varargin{:},'BlockName','MinMax','Function','Max','Inputs',mat2str(2));
11 | end
12 |
--------------------------------------------------------------------------------
/+matsim/+library/Min.m:
--------------------------------------------------------------------------------
1 | function blk = Min(varargin)
2 | %MIN Creates a simulink Min block.
3 | % Example:
4 | % in1 = Constant('var1');
5 | % in2 = Constant('var2');
6 | % blk = Min(in1,in2,'name','myMin');
7 | %
8 | % See also BINARY_OPERATOR.
9 |
10 | blk = matsim.library.binary_operator(varargin{:},'BlockName','MinMax','Function','Min','Inputs',mat2str(2));
11 | end
12 |
--------------------------------------------------------------------------------
/+matsim/+helpers/unpack.m:
--------------------------------------------------------------------------------
1 | function out = unpack(inStruct)
2 | %UNPACK Struct to cell array
3 | % C = unpack(s)
4 | % The output cell array contains both fieldnames and values of the struct
5 |
6 | tmp = cellfun(@(x) {x, inStruct.(x)},fieldnames(inStruct),'Uni',0);
7 | if isempty(tmp)
8 | out = {};
9 | else
10 | out = [tmp{:}];
11 | end
12 |
13 | end
14 |
15 |
--------------------------------------------------------------------------------
/tests/test_operations.m:
--------------------------------------------------------------------------------
1 | function [] = test_operations(sys)
2 | %TEST_OPS
3 |
4 | import matsim.library.*
5 |
6 | in = Constant(1);
7 |
8 | r = Abs(Cos().^2+Sign(in).*Sin(Constant('pi')).^2);
9 | l1 = Lookup1D('Table',[0,0],'breakpoints',[0 1]);
10 | l2 = Tan(Atan(Lookup2D(r,0,'Table','TABLE','breakpoints1',[1 2 3],'breakpoints2','[0 0.1 0.2 0.3]')));
11 | g = Gain(Max(0,{}),'Gain',-1).*Min(l1,l2);
12 |
13 | end
14 |
15 |
--------------------------------------------------------------------------------
/push.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | If [%1]==[] goto nomsg
4 | git add .
5 | git commit -m %1
6 |
7 | :Ask
8 | echo Would you like to push to remote server? (Y/N)
9 | set INPUT=
10 | set /P INPUT=Input: %=%
11 | If /I "%INPUT%"=="y" goto yes
12 | If /I "%INPUT%"=="n" goto no
13 | echo Incorrect input & goto Ask
14 |
15 | :yes
16 | git push
17 | goto exit
18 |
19 | :no
20 | goto exit
21 |
22 | :nomsg
23 | echo Please insert commit message & goto exit
24 |
25 | :exit
26 |
--------------------------------------------------------------------------------
/tools/msim_get_port.m:
--------------------------------------------------------------------------------
1 | function [port] = msim_get_port(block,index,type)
2 | %MSIM_GET_PORT Gets specified port of simulink block.
3 |
4 | if nargin < 2
5 | index = 1;
6 | end
7 | if nargin < 3
8 | type = 'all';
9 | end
10 |
11 | ports = matsim.utils.getBlockPorts(block,type);
12 |
13 | if index <= numel(ports)
14 | port = ports(index);
15 | else
16 | error('Invalid port index.')
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for Matsim
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is.
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Additional context**
14 | Add any other context or screenshots about the feature request here.
15 |
--------------------------------------------------------------------------------
/tests/test_subsystems.m:
--------------------------------------------------------------------------------
1 | function [] = test_subsystems(sys)
2 | %TEST_SUBSYSTEM
3 |
4 | import matsim.library.*
5 |
6 | v1 = REF('IN',0);
7 | s = Subsystem({{},v1},'name','TEST');
8 | s.enable(1);
9 | s.in(3,1.5);
10 | s.in(2,'name','INPUT');
11 | s.out(1,s.in(1)+s.in(3));
12 | s.out(2,{});
13 | s.out(2,'name','OUTPUT');
14 | s.trigger(Constant(-1));
15 | Terminator(s)
16 | Scope(s.outport(2))
17 | s.in(3,{});
18 |
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/+matsim/+library/Spacer.m:
--------------------------------------------------------------------------------
1 | function blk = Spacer(varargin)
2 | %SPACER Creates a virtual spacing block.
3 | % Syntax:
4 | % blk = Spacer(INPUT);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | %
7 | % Example:
8 | % in1 = Constant('var1');
9 | % blk = Gain(Spacer(in1),'Gain','Mass');
10 | %
11 | % See also UNARY_OPERATOR.
12 |
13 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Gain');
14 | blk.set('isvirtual',true);
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/+matsim/+helpers/validateArgs.m:
--------------------------------------------------------------------------------
1 | function out = validateArgs(arg)
2 | %VALIDATEARGS Convert argument list to char
3 |
4 | if ischar(arg)
5 | out = arg;
6 | elseif isnumeric(arg) || islogical(arg)
7 | out = mat2str(arg);
8 | elseif iscell(arg)
9 | out = cellfun(@matsim.helpers.validateArgs,arg,'Uni',0);
10 | elseif isstruct(arg)
11 | out = matsim.helpers.validateArgs(matsim.helpers.unpack(arg));
12 | else
13 | error('Invalid argument type')
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/tests/test_base.m:
--------------------------------------------------------------------------------
1 | function [] = test_base(sys)
2 | %TEST_BASE
3 |
4 | import matsim.library.*
5 |
6 | in = Constant(0);
7 |
8 | binary_operator({},in,'BlockName','MinMax','Inputs','2');
9 | binary_operator(in,'BlockName','MinMax','Inputs','2');
10 | binary_operator(0,1,'BlockName','MinMax','Inputs','2');
11 |
12 | unary_operator('BlockName','To Workspace');
13 | unary_operator(in,'BlockName','To Workspace','name','TEST NAME');
14 | unary_operator(0,'BlockName','To Workspace');
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/+matsim/+library/Gain.m:
--------------------------------------------------------------------------------
1 | function blk = Gain(varargin)
2 | %GAIN Creates a simulink Gain block.
3 | % Syntax:
4 | % blk = Gain(INPUT,'Gain',GAIN);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % GAIN can be number or string (variable name)
7 | %
8 | % Example:
9 | % in1 = Constant('var1');
10 | % blk = Gain(in1,'Gain','Mass');
11 | % blk = Gain(in1,'Gain',0.5);
12 | %
13 | % See also UNARY_OPERATOR.
14 |
15 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Gain');
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/+matsim/+builder/+common/isRangeOverlap.m:
--------------------------------------------------------------------------------
1 | function bool = isRangeOverlap(range1,range2)
2 | % ISRANGEOVERLAP Detect whether or not the union of two ranges have an intersection.
3 | %
4 | % Inputs:
5 | % range1 1x2 vector given with the lower value first.
6 | % range2 1x2 vector given with the lower value first.
7 | %
8 | % Outputs:
9 | % bool Logical true if the two ranges contain any common value
10 | % (including at the bounds).
11 |
12 | assert(range1(1) <= range1(2))
13 | assert(range2(1) <= range2(2))
14 |
15 | bool = range1(1) <= range2(2) && range2(1) <= range1(2);
16 | end
--------------------------------------------------------------------------------
/tools/msim_layout.m:
--------------------------------------------------------------------------------
1 | function [] = msim_layout(blocks)
2 | %MSIM_LAYOUT Layout specified blocks.
3 |
4 | if nargin < 1 || isempty(blocks)
5 | parent = get_param(gcs,'handle');
6 | matsim.builder.graphviz.simlayout(parent);
7 | else
8 | if iscell(blocks)
9 | parent = get_param(get_param(blocks{1},'parent'),'handle');
10 | else
11 | parent = get_param(get_param(blocks(1),'parent'),'handle');
12 | end
13 | if iscell(parent), parent = cell2mat(parent); end
14 | matsim.builder.graphviz.simlayout(parent,'Blocks',blocks);
15 | end
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/+matsim/+library/Demux.m:
--------------------------------------------------------------------------------
1 | function blk = Demux(varargin)
2 | %DEMUX Creates a simulink Demux block.
3 | % Syntax:
4 | % blk = Demux(INPUT,'Outputs',OUTPUTS);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % OUTPUTS can be an array or an integer specifying which output
7 | % elements of the input vector signal to extract.
8 | %
9 | % Example:
10 | % in1 = FromWorkspace('var1');
11 | % in2 = Constant('var2');
12 | % in3 = Constant('var3');
13 | % mux = Mux({in1,in2,in3});
14 | %
15 | % blk = Demux(mux,'Outputs',[1 2]);
16 | %
17 | % See also UNARY_OPERATOR.
18 |
19 | blk = matsim.library.unary_operator(varargin{:},'BlockName','Demux');
20 | end
21 |
--------------------------------------------------------------------------------
/tests/test_flow.m:
--------------------------------------------------------------------------------
1 | function [] = test_flow(sys)
2 | %TEST_FLOW
3 |
4 | import matsim.library.*
5 |
6 | in = Constant([0, 1, 2, 3]);
7 | in.outport(1,'name','CONSTANT')
8 |
9 | r = REF('in',in);
10 | d = Demux(r,'Outputs',[1,3]);
11 | m = Mux({d.outport(2)+1,-1});
12 | % s = Selector(m,'Indices',[1,4],'InputPortWidth',4);
13 | s = m([1,4],4);
14 | w = Switch(s,0);
15 | g = Merge({w,{},s});
16 | g.outport(1,'name','MERGE')
17 |
18 | bc = BusCreator({r,g,w});
19 | bs = BusSelector(bc,'OutputSignals',{'CONSTANT','MERGE','CONSTANT','MERGE'});
20 | Terminator(Gain(bs.outport(1)));
21 | Terminator(Gain(bs.outport(2)));
22 | Scope({bs.outport(3), bs.outport(4)});
23 |
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/+matsim/+library/ToWorkspace.m:
--------------------------------------------------------------------------------
1 | function blk = ToWorkspace(varargin)
2 | %TOWORKSPACE Creates a simulink ToWorkspace block.
3 | % Syntax:
4 | % blk = ToWorkspace(INPUT,'VariableName',VARIABLENAME);
5 | % VARIABLENAME may be numeric or string
6 | % blk = ToWorkspace(INPUT,'VariableName',VARIABLENAME,ARGS);
7 | % ARGS is an optional list of parameter/value pairs specifying simulink
8 | % block properties.
9 | %
10 | % Example:
11 | % in1 = Constant('var1');
12 | % blk = ToWorkspace(in1,'VariableName','varOut','parent',gcs);
13 | % blk = ToWorkspace(in1,'VariableName','varOut2','Name','myVar','BackgroundColor','red');
14 | %
15 | % See also UNARY_OPERATOR.
16 |
17 | blk = matsim.library.unary_operator(varargin{:},'BlockName','To Workspace');
18 | end
19 |
--------------------------------------------------------------------------------
/+matsim/+utils/getversion.m:
--------------------------------------------------------------------------------
1 | function [major_version, minor_version, version_number] = getversion()
2 | %GETVERSION Gets MATLAB version (e.g. R2015B)
3 |
4 | persistent cachedMatlabVersion
5 | if isempty(cachedMatlabVersion)
6 | cachedMatlabVersion = struct;
7 | version_string = version;
8 | tokens = regexp(version_string,'\(R([0-9]*)(a|b)\)', 'once', 'tokens'); % ['2011', 'b']
9 | cachedMatlabVersion.version_string = version_string;
10 | cachedMatlabVersion.version_number = sscanf(version_string,'%f',1);
11 | cachedMatlabVersion.major_version = str2double(tokens{1});
12 | cachedMatlabVersion.minor_version = tokens{2};
13 | end
14 |
15 | major_version = cachedMatlabVersion.major_version;
16 | minor_version = cachedMatlabVersion.minor_version;
17 | version_number = cachedMatlabVersion.version_number;
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/tests/test_run.m:
--------------------------------------------------------------------------------
1 | % function [] = example_1()
2 |
3 | % Init
4 | import matsim.library.*
5 |
6 | % Create or load model
7 | sys = simulation.load('matsim_model');
8 | sys.setSolver('Ts',0.01,'DiscreteOnly',true)
9 | sys.clear()
10 | sys.show()
11 |
12 | %% Create
13 | [script_path,script_name] = fileparts(which('test_run'));
14 | test_files = dir(fullfile(script_path,'*.m'));
15 | test_names = setdiff(strrep({test_files.name},'.m',''),script_name);
16 | test_numbered = arrayfun(@(i) sprintf('%d. %s',i,test_names{i}),1:length(test_names),'uni',0);
17 | fprintf('Available tests:\n\t%s\n',strjoin(test_numbered,sprintf('\n\t')))
18 | test = input('Select test: ','s');
19 | if ~isnan(str2double(test))
20 | feval(test_names{str2double(test)},sys)
21 | else
22 | feval(lower(test),sys)
23 | end
24 |
25 | %% Layout & connect
26 | sys.layout()
27 |
28 |
--------------------------------------------------------------------------------
/generator/source.txt:
--------------------------------------------------------------------------------
1 | classdef %s < matsim.library.block
2 | %%%s Creates a simulink %s block.
3 | %% Example:
4 | %% blk = %s('Name','my%s');
5 | %%
6 | %% See also BLOCK.
7 |
8 | properties
9 |
10 | end
11 |
12 | methods
13 | function this = %s(varargin)
14 | p = inputParser;
15 | p.CaseSensitive = false;
16 | p.KeepUnmatched = true;
17 | %s
18 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
19 | parse(p,varargin{:})
20 |
21 | %s
22 | parent = p.Results.parent;
23 | args = matsim.helpers.validateArgs(p.Unmatched);
24 |
25 | this = this@matsim.library.block('model','%s','BlockName','%s','parent',parent,args{:});
26 |
27 | %s
28 | end
29 | end
30 | end
31 |
32 |
--------------------------------------------------------------------------------
/generator/unary.txt:
--------------------------------------------------------------------------------
1 | classdef %s < matsim.library.unary_operator
2 | %%%s Creates a simulink %s block.
3 | %% Example:
4 | %% input = Constant('var1');
5 | %% blk = %s(input,'Name','my%s');
6 | %%
7 | %% See also UNARY_OPERATOR.
8 |
9 | properties
10 |
11 | end
12 |
13 | methods
14 | function this = %s(varargin)
15 | p = inputParser;
16 | p.CaseSensitive = false;
17 | p.KeepUnmatched = true;
18 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
19 | %s
20 | parse(p,varargin{:})
21 |
22 | b1 = p.Results.b1;
23 | %s
24 | args = matsim.helpers.validateArgs(p.Unmatched);
25 |
26 | this = this@matsim.library.unary_operator(b1,'BlockName','%s',args{:});
27 |
28 | %s
29 | end
30 | end
31 |
32 | end
33 |
34 |
--------------------------------------------------------------------------------
/+matsim/+helpers/getBlockPath.m:
--------------------------------------------------------------------------------
1 | function [path] = getBlockPath(object)
2 | %GETBLOCKPATH Gets simulink object path
3 | % path = getBlockPath(object)
4 | % Object can be of type "simulation", "block", "handle", "string"
5 |
6 | if isa(object,'matsim.library.simulation')
7 | path = get(object,'Name');
8 | elseif isa(object,'matsim.library.block')
9 | path = strjoin({get(object,'Path'),get(object,'Name')},'/');
10 | elseif isa(object,'matsim.library.block_input')
11 | path = strjoin({get(object,'Path'),get(object,'Name')},'/');
12 | elseif ishandle(object)
13 | if strcmp(get(object,'Type'),'block_diagram')
14 | path = get(object,'Name');
15 | else
16 | path = strjoin({get(object,'Path'),get(object,'Name')},'/');
17 | end
18 | elseif ischar(object)
19 | path = object;
20 | else
21 | error('Invalid object')
22 | end
23 |
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/+matsim/+library/simOutput.m:
--------------------------------------------------------------------------------
1 | classdef simOutput < handle
2 | %SIMOUTPUT Holds simulation output info (e.g. logged signals).
3 |
4 | properties (Access = private)
5 | simOut
6 | end
7 |
8 | methods
9 | function this = simOutput(simOut)
10 | this.simOut = simOut;
11 | end
12 |
13 | function out = Raw(this)
14 | out = this.simOut;
15 | end
16 |
17 | function logsOut = Logs(this)
18 | % Return logged data as struct
19 | logsOut = struct;
20 | log = get(this.simOut,'logsout');
21 | if ~isempty(log)
22 | for e = 1:log.getLength
23 | name = log.get(e).Name;
24 | logsOut.(genvarname(name)) = eval('log.get(e).Values');
25 | logsOut.(genvarname(name)).Name = name;
26 | end
27 | end
28 | end
29 | end
30 |
31 | end
32 |
--------------------------------------------------------------------------------
/+matsim/+helpers/validateInputs.m:
--------------------------------------------------------------------------------
1 | function out = validateInputs(input,parent)
2 | %VALIDATEINPUTS Convert input to block_input
3 |
4 | if isempty(input)
5 | out = matsim.library.block_input({});
6 | elseif iscell(input)
7 | out = cellfun(@(in) matsim.helpers.validateInputs(in,parent),input,'Uni',0);
8 | elseif isnumeric(input)
9 | out = matsim.library.block_input(matsim.library.Constant(input,'parent',parent));
10 | elseif isa(input,'matsim.library.block') || isa(input,'matsim.library.block_input')
11 | if strcmp(input.get('BlockType'),'Goto')
12 | out = matsim.library.block_input(matsim.library.REF(input.get('GotoTag')));
13 | elseif isa(input,'matsim.library.block')
14 | out = matsim.library.block_input(input,input.outport);
15 | elseif isa(input,'matsim.library.block_input')
16 | out = input;
17 | end
18 | else
19 | error('Invalid argument type')
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/+matsim/+utils/getBlockPorts.m:
--------------------------------------------------------------------------------
1 | function [ports] = getBlockPorts(block,type)
2 | %GETBLOCKPORTS Returns block port handles as array
3 |
4 | h = get_param(block,'porthandles');
5 |
6 | switch type
7 | case 'all'
8 | if isfield(h,'Reset')
9 | ports = [h.Inport, h.Enable, h.Trigger, h.Reset, h.Ifaction, h.Outport];
10 | else
11 | ports = [h.Inport, h.Enable, h.Trigger, h.Ifaction, h.Outport];
12 | end
13 | case 'input'
14 | if isfield(h,'Reset')
15 | ports = [h.Inport, h.Enable, h.Trigger, h.Reset, h.Ifaction];
16 | else
17 | ports = [h.Inport, h.Enable, h.Trigger, h.Ifaction];
18 | end
19 | case 'output'
20 | ports = [h.Outport];
21 | case 'special'
22 | if isfield(h,'Reset')
23 | ports = [h.Enable, h.Trigger, h.Reset, h.Ifaction];
24 | else
25 | ports = [h.Enable, h.Trigger, h.Ifaction];
26 | end
27 | otherwise
28 | error('Invalid port type.')
29 | end
30 |
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/+matsim/+builder/+graphviz/sim2adj.m:
--------------------------------------------------------------------------------
1 | function [adjMatrix, blocksToLayout] = sim2adj(sys,blocksToLayout)
2 | %SIM2ADJ simulink system to adjacency matrix
3 |
4 | if isempty(blocksToLayout)
5 | all_blocks = matsim.helpers.findBlock(sys,'SearchDepth',1);
6 | all_blocks = all_blocks(all_blocks ~= sys); % Remove self
7 | blocksToLayout = all_blocks;
8 | end
9 |
10 | adjMatrix = cell(length(blocksToLayout),length(blocksToLayout));
11 |
12 | for i=1:length(blocksToLayout)
13 | set(blocksToLayout(i),'Tag',mat2str(i));
14 | end
15 |
16 | for i=1:length(blocksToLayout)
17 | neighbours = matsim.builder.graphviz.getNeighbours(sys,blocksToLayout(i));
18 | for j=1:size(neighbours,1)
19 | if neighbours(j,1) == -1, continue, end;
20 | if ~ismember(neighbours(j,1),blocksToLayout), continue, end;
21 | col = str2double(get(neighbours(j,1),'Tag'));
22 | adjMatrix{i,col} = neighbours(j,:);
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/+matsim/+library/EnabledSubsystem.m:
--------------------------------------------------------------------------------
1 | function blk = EnabledSubsystem(varargin)
2 | %SUBSYSTEM Creates a simulink Subsystem block with enable port.
3 | % Syntax:
4 | % Same as Subsystem.
5 | %
6 | % Example:
7 | % in1 = Constant('var1');
8 | % en = Delay(Constant(1));
9 | % s = EnabledSubsystem({in1},'Enable',en); % Subsystem with one inport
10 | %
11 | % See also Subsystem.
12 |
13 | p = inputParser;
14 | p.CaseSensitive = false;
15 | p.KeepUnmatched = true;
16 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
17 | addParamValue(p,'Enable',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
18 | parse(p,varargin{:})
19 |
20 | inputs = p.Results.inputs;
21 | enable = p.Results.Enable;
22 | args = matsim.helpers.unpack(p.Unmatched);
23 |
24 | blk = matsim.library.Subsystem(inputs,args{:});
25 | ports = blk.getPorts();
26 |
27 | if matsim.helpers.isArgSpecified(p,'Enable') || isempty(ports.enable)
28 | blk.enable(enable);
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Marco Gavelli
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/generator/binary.txt:
--------------------------------------------------------------------------------
1 | classdef %s < matsim.library.binary_operator
2 | %%%s Creates a simulink %s block.
3 | %% Example:
4 | %% input1 = Constant('var1');
5 | %% input2 = Constant(1);
6 | %% blk = %s(input1,input2,'Name','my%s');
7 | %%
8 | %% See also BINARY_OPERATOR.
9 |
10 | properties
11 |
12 | end
13 |
14 | methods
15 | function this = %s(varargin)
16 | p = inputParser;
17 | p.CaseSensitive = false;
18 | p.KeepUnmatched = true;
19 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
20 | addOptional(p,'b2',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
21 | %s
22 | parse(p,varargin{:})
23 |
24 | b1 = p.Results.b1;
25 | b2 = p.Results.b2;
26 | %s
27 | args = matsim.helpers.validateArgs(p.Unmatched);
28 |
29 | this = this@matsim.library.binary_operator(b1,b2,'BlockName','%s','Function','Max','Inputs',mat2str(2),args{:});
30 |
31 | %s
32 | end
33 | end
34 |
35 | end
36 |
37 |
--------------------------------------------------------------------------------
/example_1.m:
--------------------------------------------------------------------------------
1 | %% Init
2 | close all
3 |
4 | import matsim.library.*
5 |
6 | % Create or load model
7 | sys = simulation.load('matsim_model');
8 | sys.setSolver('Ts',0.01,'DiscreteOnly',true)
9 | sys.clear()
10 | sys.show()
11 |
12 | %% Create
13 | Vx = FromWorkspace('V_x'); % Add FromWorkspace and Constant blocks
14 | Wr = FromWorkspace('W_r');
15 | Rr = Constant(0.32);
16 |
17 | slip = 1 - Vx./(Wr.*Rr); % Evaluate complex mathematical expression
18 | slip.outport(1,'name','slip'); % Set signal name and line label
19 | sys.log(slip) % Log the output of the "slip" block
20 |
21 | s = Scope(slip); % Create and open scope block
22 | s.open()
23 |
24 | %% Layout & connect
25 | sys.layout() % Connect and layout model
26 |
27 | %% Simulate the system
28 | V_x = [0:0.1:10;linspace(5,20,101)]'; % Define input variables
29 | W_r = [0:0.1:10;linspace(5,23,101)/0.32]';
30 | simOut = sys.run('StopTime',10).Logs; % Simulate the system
31 |
32 | figure
33 | hold on
34 | grid on
35 | plot(simOut.slip)
36 |
37 | % sys.save()
38 | % sys.close()
39 |
40 |
--------------------------------------------------------------------------------
/+matsim/+helpers/getValidParent.m:
--------------------------------------------------------------------------------
1 | function parent = getValidParent(varargin)
2 | %GETVALIDPARENT Find a valid block parent in argument list
3 | % parent = getValidParent(obj1,obj2,obj3,...)
4 | % Parameters:
5 | % A list containing objects of type:
6 | % "simulation", "block", "handle", "string"
7 |
8 | parent = '';
9 | for i = 1:length(varargin)
10 | if isa(varargin{i},'matsim.library.simulation')
11 | parent = get(varargin{i},'Name');
12 | return;
13 | elseif isa(varargin{i},'matsim.library.block')
14 | parent = get(varargin{i},'Path');
15 | return;
16 | elseif isa(varargin{i},'matsim.library.block_input')
17 | parent = get(varargin{i},'Path');
18 | return;
19 | elseif ishandle(varargin{i})
20 | if strcmp(get(varargin{i},'type'),'block_diagram')
21 | parent = get(varargin{i},'Name');
22 | elseif strcmp(get(varargin{i},'type'),'matsim.library.block')
23 | parent = get(varargin{i},'Path');
24 | end
25 | return;
26 | elseif ischar(varargin{i})
27 | parent = varargin{i};
28 | return;
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/+matsim/+library/block_input.m:
--------------------------------------------------------------------------------
1 | classdef block_input
2 | %BLOCK_INPUT Holds input info (input block, source port, input type).
3 |
4 | properties (Access = public)
5 | % Input block
6 | value
7 | % Source index port
8 | srcport
9 | % Port type
10 | type
11 | end
12 |
13 | methods
14 | function this = block_input(varargin)
15 | p = inputParser;
16 | p.CaseSensitive = false;
17 | p.KeepUnmatched = true;
18 | addRequired(p,'value',@(x) isempty(x) || isa(x,'matsim.library.block'));
19 | addOptional(p,'srcport',1,@isnumeric);
20 | addOptional(p,'type','inport',@ischar);
21 | parse(p,varargin{:})
22 |
23 | this.value = p.Results.value;
24 | this.srcport = p.Results.srcport;
25 | this.type = p.Results.type;
26 | end
27 |
28 | function h = handle(this)
29 | h = this.value.handle;
30 | end
31 | function p = get(this,prop)
32 | p = get(this.value.handle,prop);
33 | end
34 | function [] = set(this,prop,value,idx)
35 | this.value.set(prop,value,idx);
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/+matsim/+utils/cell2str.m:
--------------------------------------------------------------------------------
1 | function string = cell2str(celldata)
2 | %CELL2STR Convert a 2-D cell array of strings to a string in MATLAB syntax.
3 | % STR = CELL2STR(CELLSTR) converts the 2-D cell-string CELLSTR to a
4 | % MATLAB string so that EVAL(STR) produces the original cell-string.
5 | % Works as corresponding MAT2STR but for cell array of strings instead of
6 | % scalar matrices.
7 | %
8 | % Example
9 | % cellstr = {'U-234','Th-230'};
10 | % cell2str(cellstr) produces the string '{''U-234'',''Th-230'';}'.
11 | %
12 | % See also MAT2STR, STRREP, CELLFUN, EVAL.
13 | % Developed by Per-Anders Ekstr?m, 2003-2007 Facilia AB.
14 | if nargin~=1
15 | error('CELL2STR:Nargin','Takes 1 input argument.');
16 | end
17 | if isempty(celldata)
18 | string = '{}';
19 | return
20 | end
21 | if ischar(celldata)
22 | string = ['''' celldata ''''];
23 | return
24 | end
25 | if ~isvector(celldata)
26 | error('CELL2STR:OneDInput','Input cell array must be 1-D.');
27 | end
28 | for i=1:length(celldata)
29 | if ischar(celldata{i})
30 | celldata{i} = ['''' celldata{i} ''','];
31 | else
32 | celldata{i} = [mat2str(celldata{i}) ','];
33 | end
34 | end
35 | celldata = celldata';
36 | string = ['{' celldata{:} '}'];
37 | end
38 |
--------------------------------------------------------------------------------
/generator/multiple.txt:
--------------------------------------------------------------------------------
1 | classdef %s < matsim.library.block
2 | %%%s Creates a simulink %s block.
3 | %% Example:
4 | %% input1 = Constant(1);
5 | %% input2 = Constant(2);
6 | %% input3 = FromWorkspace('var3');
7 | %% blk = %s({input1,input2,input3},'Name','my%s');
8 | %%
9 | %% See also BLOCK.
10 |
11 | properties
12 |
13 | end
14 |
15 | methods
16 | function this = %s(varargin)
17 | p = inputParser;
18 | p.CaseSensitive = false;
19 | p.KeepUnmatched = true;
20 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
21 | %s
22 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
23 | parse(p,varargin{:})
24 |
25 | inputs = p.Results.inputs;
26 | if ~iscell(inputs)
27 | inputs = {inputs};
28 | end
29 |
30 | %s
31 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
32 | args = matsim.helpers.validateArgs(p.Unmatched);
33 |
34 | if isempty(parent)
35 | parent = gcs;
36 | end
37 |
38 | this = this@matsim.library.block('model','%s','BlockName','%s','parent',parent,args{:});
39 | if matsim.helpers.isArgSpecified(p,'inputs')
40 | this.setInputs(inputs);
41 | end
42 |
43 | %s
44 | end
45 | end
46 | end
47 |
48 |
--------------------------------------------------------------------------------
/+matsim/+library/SwitchCase.m:
--------------------------------------------------------------------------------
1 | classdef SwitchCase < matsim.library.unary_operator
2 | %SWITCHCASE Creates a simulink SwitchCase block.
3 | % Syntax:
4 | % blk = SwitchCase(INPUT,'CaseConditions',CONDITIONS);
5 | % CONDITIONS must be a cell array containing numeric arrays
6 | % blk = SwitchCase(INPUT,'CaseConditions',CONDITIONS,ARGS);
7 | % ARGS is an optional list of parameter/value pairs specifying simulink
8 | % block properties.
9 | %
10 | % Example:
11 | % in1 = Constant('var1');
12 | % blk = SwitchCase(in1,'CaseConditions',{1,[2,3]});
13 | %
14 | % See also UNARY_OPERATOR.
15 |
16 | properties
17 |
18 | end
19 |
20 | methods
21 | function this = SwitchCase(varargin)
22 | p = inputParser;
23 | p.CaseSensitive = false;
24 | p.KeepUnmatched = true;
25 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
26 | addParamValue(p,'CaseConditions',{},@(x) iscell(x) || isnumeric(x));
27 | parse(p,varargin{:})
28 |
29 | b1 = p.Results.b1;
30 | CaseConditions = p.Results.CaseConditions;
31 | args = matsim.helpers.validateArgs(p.Unmatched);
32 |
33 | this = this@matsim.library.unary_operator(b1,'BlockName','Switch Case',args{:});
34 |
35 | if ~isempty(CaseConditions)
36 | if ~iscell(CaseConditions), CaseConditions={CaseConditions}; end
37 | this.set('CaseConditions',matsim.utils.cell2str(CaseConditions))
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/+matsim/+library/FromWorkspace.m:
--------------------------------------------------------------------------------
1 | classdef FromWorkspace < matsim.library.block
2 | %FROMWORKSPACE Creates a simulink FromWorkspace block.
3 | % Syntax:
4 | % blk = FromWorkspace(VARIABLENAME);
5 | % VARIABLENAME may be numeric or string
6 | % blk = FromWorkspace(VARIABLENAME, ARGS);
7 | % ARGS is an optional list of parameter/value pairs specifying simulink
8 | % block properties.
9 | %
10 | % Example:
11 | % blk = FromWorkspace('var1','parent',gcs);
12 | % blk = FromWorkspace('var1','Name','myVar','BackgroundColor','red');
13 | %
14 | % See also BLOCK.
15 |
16 | properties
17 |
18 | end
19 |
20 | methods
21 | function this = FromWorkspace(varargin)
22 | p = inputParser;
23 | p.CaseSensitive = false;
24 | p.KeepUnmatched = true;
25 | addRequired(p,'VariableName',@(x) ischar(x) || isnumeric(x));
26 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
27 | parse(p,varargin{:})
28 |
29 | VariableName = p.Results.VariableName;
30 | parent = p.Results.parent;
31 | args = matsim.helpers.validateArgs(p.Unmatched);
32 |
33 | if isempty(parent)
34 | parent = gcs;
35 | end
36 |
37 | this = this@matsim.library.block('BlockType','FromWorkspace','parent',parent,args{:});
38 |
39 | if ~isempty(VariableName)
40 | this.set('VariableName',VariableName)
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/tools/msim_createsubsystem.m:
--------------------------------------------------------------------------------
1 | function [] = msim_createsubsystem(blocks)
2 | %MSIM_CREATESUBSYSTEM Create subsystem from blocks.
3 |
4 | bh = get_param(blocks,'handle');
5 | if iscell(bh), bh = cell2mat(bh); end
6 | pre_handles = find_system(get(bh(1),'parent'),'findall','on','searchdepth',1,'type','block','blocktype','SubSystem');
7 | Simulink.BlockDiagram.createSubSystem(bh);
8 | post_handles = find_system(get(bh(1),'parent'),'findall','on','searchdepth',1,'type','block','blocktype','SubSystem');
9 | new_subsys = setdiff(post_handles,pre_handles);
10 | if numel(new_subsys)~=1
11 | error('Something strange happened.')
12 | end
13 |
14 | inports = find_system(new_subsys,'findall','on','searchdepth',1,'type','block','blocktype','Inport');
15 | outports = find_system(new_subsys,'findall','on','searchdepth',1,'type','block','blocktype','Outport');
16 |
17 | for in = 1:numel(inports)
18 | ph = get(inports(in),'portconnectivity');
19 | if ~strcmp(get(ph.DstBlock,'blocktype'),'SubSystem'), continue, end
20 | inport_old = find_system(ph.DstBlock,'findall','on','searchdepth',1,'type','block','blocktype','Inport','Port',num2str(ph.DstPort+1));
21 | set(inports(in),'name',get(inport_old,'name'))
22 | end
23 | for out = 1:numel(outports)
24 | ph = get(outports(out),'portconnectivity');
25 | if ~strcmp(get(ph.SrcBlock,'blocktype'),'SubSystem'), continue, end
26 | outport_old = find_system(ph.SrcBlock,'findall','on','searchdepth',1,'type','block','blocktype','Outport','Port',num2str(ph.SrcPort+1));
27 | set(outports(out),'name',get(outport_old,'name'))
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/tools/msim_align.m:
--------------------------------------------------------------------------------
1 | function [] = msim_align(hBlocks)
2 | %MSIM_ALIGN Align blocks to inport or outport.
3 | % Syntax:
4 | % msim_align(BLOCKS);
5 | % BLOCKS is a vector containing the blocks to align
6 | % BLOCKS can be:
7 | % - a cell array of strings
8 | % - a double array of block handles
9 |
10 | if iscellstr(hBlocks)
11 | hBlocks = cellfun(@(b) get_param(b,'handle'),hBlocks,'uni',1);
12 | end
13 |
14 | for bb = 1:numel(hBlocks)
15 | ports = get(hBlocks(bb),'porthandles');
16 | if numel(ports.Outport)==1
17 | line = get(ports.Outport,'Line');
18 | if line==-1, continue, end
19 | dst = get(line,'DstPortHandle');
20 | if numel(dst)~=1 || dst==-1, continue, end
21 | blk_pos = get(hBlocks(bb),'Position');
22 | port_pos = get(dst,'Position');
23 | new_y = port_pos(2)-(blk_pos(4)-blk_pos(2))/2;
24 | new_h = port_pos(2)+(blk_pos(4)-blk_pos(2))/2;
25 | set(hBlocks(bb),'Position',[blk_pos(1) new_y blk_pos(3) new_h])
26 | elseif numel(ports.Inport)==1
27 | line = get(ports.Inport,'Line');
28 | if line==-1, continue, end
29 | src = get(line,'SrcPortHandle');
30 | if numel(src)~=1 || src==-1, continue, end
31 | blk_pos = get(hBlocks(bb),'Position');
32 | port_pos = get(src,'Position');
33 | new_y = port_pos(2)-(blk_pos(4)-blk_pos(2))/2;
34 | new_h = port_pos(2)+(blk_pos(4)-blk_pos(2))/2;
35 | set(hBlocks(bb),'Position',[blk_pos(1) new_y blk_pos(3) new_h])
36 | else
37 | continue
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
27 |
28 |
29 |
30 |
38 |
39 |
40 |
41 |
42 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/+matsim/+library/Lookup1D.m:
--------------------------------------------------------------------------------
1 | classdef Lookup1D < matsim.library.unary_operator
2 | %LOOKUP1D Creates a simulink 1-D Lookup Table block.
3 | % Syntax:
4 | % blk = Lookup1D(INPUT,'Table',TABLE,'breakpoints',BREAKPOINTS);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % TABLE is a numeric array or string (variable name) to be set as table
7 | % data.
8 | % BREAKPOINTS is a numeric array or string (variable name) to be set as
9 | % table x-data.
10 | %
11 | % Example:
12 | % in1 = FromWorkspace('var1');
13 | % blk = Lookup1D(in1,'Table',rand(1,4),'breakpoints',[1:4]);
14 | %
15 | % See also UNARY_OPERATOR.
16 |
17 | properties
18 |
19 | end
20 |
21 | methods
22 | function this = Lookup1D(varargin)
23 | p = inputParser;
24 | p.CaseSensitive = false;
25 | p.KeepUnmatched = true;
26 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
27 | addParamValue(p,'Table','',@(x) isnumeric(x) || ischar(x));
28 | addParamValue(p,'breakpoints','',@(x) isnumeric(x) || ischar(x));
29 | parse(p,varargin{:})
30 |
31 | b1 = p.Results.b1;
32 | Table = p.Results.Table;
33 | breakpoints = p.Results.breakpoints;
34 | args = matsim.helpers.validateArgs(p.Unmatched);
35 |
36 | this = this@matsim.library.unary_operator(b1,'BlockName','1-D Lookup Table',args{:});
37 | this.set('ExtrapMethod','Clip')
38 | this.set('UseLastTableValue','on')
39 |
40 | this.set('Table',Table)
41 | this.set('BreakpointsForDimension1',breakpoints)
42 | end
43 | end
44 |
45 | end
46 |
--------------------------------------------------------------------------------
/generator/ReadTable.m:
--------------------------------------------------------------------------------
1 | function [out] = ReadTable()
2 | % https://it.mathworks.com/help/simulink/slref/block-specific-parameters.html
3 | blocks = matsim.utils.handlevar([]);
4 | fid = fopen('blockparams.csv');
5 |
6 | try
7 | tline = fgetl(fid);
8 | while ischar(tline)
9 | fields = strsplit(tline,'@');
10 | if isempty(fields)
11 | % warning(['Invalid line: ', tline])
12 | elseif ~isempty(strfind(fields{1},'Block (Type)'))
13 | % Skip
14 | elseif length(fields) == 1
15 | [~,nt]=regexp(fields{1},'(.*)\((.*)\)','match','tokens');
16 | block = matsim.utils.handlevar(struct('params',struct('name',{},'dialog',{},'type',{})));
17 | blocks(end+1) = block; %#ok
18 | if isempty(nt)
19 | block.Value.name = strtrim(tline);
20 | block.Value.type = '';
21 | else
22 | block.Value.name = strtrim(nt{1}{1});
23 | block.Value.type = strtrim(nt{1}{2});
24 | end
25 | elseif length(fields) == 3
26 | par = struct;
27 | par.name = strtrim(fields{1});
28 | par.dialog = strtrim(fields{2});
29 | par.type = strtrim(fields{3});
30 | block.Value.params(end+1) = par;
31 | else
32 | % warning(['Invalid line: ', tline])
33 | end
34 | tline = fgetl(fid);
35 | end
36 | catch ME
37 | fclose(fid);
38 | warning(['Error on line: ', tline])
39 | error(ME.message)
40 | end
41 |
42 | out = [blocks.Value];
43 | end
44 |
45 |
--------------------------------------------------------------------------------
/tools/fun2model.m:
--------------------------------------------------------------------------------
1 | function [out] = fun2model(varargin)
2 | %FUN2MODEL Function handle to simulink model
3 |
4 | import matsim.library.*
5 |
6 | p = inputParser;
7 | p.CaseSensitive = true;
8 | p.KeepUnmatched = true;
9 | addRequired(p,'fun',@(x) isa(x,'function_handle'));
10 | addParamValue(p,'model','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
11 | addParamValue(p,'goto',false,@islogical);
12 | parse(p,varargin{:})
13 |
14 | fun = p.Results.fun;
15 | goto = p.Results.goto;
16 | model = matsim.helpers.getBlockPath(p.Results.model);
17 | args = matsim.helpers.unpack(p.Unmatched);
18 |
19 | if isempty(model)
20 | model = simulation.load('untitled');
21 | % model.clear()
22 | model.show()
23 | end
24 |
25 | funstr = func2str(fun);
26 | [~,tok] = regexp(funstr,'@\(([\w|,]*)\)','match','tokens');
27 | symb_names = strtrim(strsplit(tok{1}{1},','));
28 |
29 | subsystem = Subsystem('parent',model,args{:});
30 | set_param(0,'CurrentSystem',subsystem.handle)
31 | for e=1:length(symb_names)
32 | eval([symb_names{e}, '=subsystem.in(e,''name'',symb_names{e});'])
33 | if (goto)
34 | eval([symb_names{e}, sprintf('=REF(''%s'', %s);',symb_names{e},symb_names{e})])
35 | end
36 | end
37 |
38 | [~,tok] = regexp(funstr,'(@\([\w|,]*\))','match','tokens');
39 | exprstr = strrep(funstr,tok{1}{1},'');
40 | [~,~,pos] = regexp(exprstr,'(\w+\()','match','tokens'); % try use matsim functions
41 | exprstr(pos) = upper(exprstr(pos));
42 | res = eval(exprstr);
43 | subsystem.out(1,res,'name','res')
44 |
45 | matsim.builder.graphviz.simlayout(subsystem.handle,'Recursive',true)
46 | out = subsystem;
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/+matsim/+library/Selector.m:
--------------------------------------------------------------------------------
1 | classdef Selector < matsim.library.unary_operator
2 | %SELECTOR Creates a simulink Selector block.
3 | % Syntax:
4 | % blk = Selector(INPUT,'Indices',INDICES,'InputPortWidth',PORTWIDTH);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % INPUT can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % If INPUT is a number a Constant block with that value will
11 | % be created.
12 | % INDICES parameter selects which elements to extract from the input
13 | % vector.
14 | % PORTWIDTH is the size of the input vector.
15 | %
16 | % Example:
17 | % in1 = Constant([1,2,3]);
18 | % Selector(in1,'Indices',[1,3],'InputPortWidth',3);
19 | %
20 | % See also UNARY_OPERATOR.
21 |
22 | properties
23 |
24 | end
25 |
26 | methods
27 | function this = Selector(varargin)
28 | p = inputParser;
29 | p.CaseSensitive = false;
30 | p.KeepUnmatched = true;
31 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
32 | addParamValue(p,'Indices',[1,3],@(x) isnumeric(x));
33 | addParamValue(p,'InputPortWidth',3,@(x) isnumeric(x));
34 | parse(p,varargin{:})
35 |
36 | b1 = p.Results.b1;
37 | Indices = p.Results.Indices;
38 | InputPortWidth = p.Results.InputPortWidth;
39 | args = matsim.helpers.validateArgs(p.Unmatched);
40 |
41 | this = this@matsim.library.unary_operator(b1,'BlockName','Selector',args{:});
42 | this.set({'NumberOfDimensions','1','IndexOptions','Index vector (dialog)','InputPortWidth',mat2str(InputPortWidth),'Indices',mat2str(Indices)})
43 | end
44 | end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/+matsim/+library/Constant.m:
--------------------------------------------------------------------------------
1 | classdef Constant < matsim.library.block
2 | %CONSTANT Creates a simulink Constant block.
3 | % Syntax:
4 | % blk = Constant(VALUE);
5 | % VALUE may be numeric or string (variable name)
6 | % blk = Constant(VALUE,ARGS);
7 | % ARGS is an optional list of parameter/value pairs specifying simulink
8 | % block properties.
9 | %
10 | % Example:
11 | % blk = Constant(0);
12 | % blk = Constant('var1','parent',gcs);
13 | % blk = Constant(-1,'Name','myConst','BackgroundColor','red');
14 | %
15 | % See also BLOCK.
16 |
17 | properties
18 |
19 | end
20 |
21 | methods
22 | function this = Constant(varargin)
23 | p = inputParser;
24 | p.CaseSensitive = false;
25 | p.KeepUnmatched = true;
26 | addRequired(p,'Value',@(x) ischar(x) || isnumeric(x));
27 | addParamValue(p,'FitSize',false,@(x) islogical(x));
28 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
29 | parse(p,varargin{:})
30 |
31 | Value = p.Results.Value;
32 | FitSize = p.Results.FitSize;
33 | parent = p.Results.parent;
34 | args = matsim.helpers.validateArgs(p.Unmatched);
35 |
36 | if isempty(parent)
37 | parent = gcs;
38 | end
39 |
40 | this = this@matsim.library.block('BlockType','Constant','parent',parent,args{:});
41 | if ~isempty(Value)
42 | this.set({'Value',Value,'VectorParams1D','off'})
43 | if FitSize
44 | location = this.get('Position');
45 | location(3) = location(1)+max(40,10*length(Value));
46 | this.set('Position',location);
47 | end
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/+matsim/+library/Integrator.m:
--------------------------------------------------------------------------------
1 | classdef Integrator < matsim.library.unary_operator
2 | %INTEGRATOR Creates a simulink Integrator block.
3 | % Syntax:
4 | % blk = Integrator(INPUT,'SampleTime',SAMPLETIME,'x0',X0);
5 | % The block specified as INPUT will be connected to the input port of
6 | % this block.
7 | % SAMPLETIME is optional (integer). If the SampleTime is specified a
8 | % "Discrete-Time Integrator" will be created, otherwise an "Integrator"
9 | % block will be created.
10 | % X0 is a number that will be set as block's Initial Condition.
11 | %
12 | % Example:
13 | % in1 = FromWorkspace('var1');
14 | % blk = Integrator(in1,'SampleTime',-1,'x0',0);
15 | %
16 | % See also UNARY_OPERATOR.
17 |
18 | properties
19 |
20 | end
21 |
22 | methods
23 | function this = Integrator(varargin)
24 | p = inputParser;
25 | p.CaseSensitive = false;
26 | p.KeepUnmatched = true;
27 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
28 | addParamValue(p,'SampleTime',0,@isnumeric);
29 | addParamValue(p,'x0',0,@isnumeric);
30 | parse(p,varargin{:})
31 |
32 | b1 = p.Results.b1;
33 | SampleTime = p.Results.SampleTime;
34 | x0 = p.Results.x0;
35 | args = matsim.helpers.validateArgs(p.Unmatched);
36 |
37 | if SampleTime ~= 0
38 | integ = 'Discrete-Time Integrator';
39 | else
40 | integ = 'Integrator';
41 | end
42 |
43 | this = this@matsim.library.unary_operator(b1,'BlockName',integ,args{:});
44 | this.set('InitialCondition',mat2str(x0));
45 | if SampleTime ~= 0
46 | this.set('SampleTime',mat2str(SampleTime));
47 | end
48 | end
49 | end
50 |
51 | end
52 |
--------------------------------------------------------------------------------
/+matsim/+library/BusSelector.m:
--------------------------------------------------------------------------------
1 | classdef BusSelector < matsim.library.unary_operator
2 | %BUSSELECTOR Creates a simulink Bus Selector block.
3 | % Syntax:
4 | % blk = BusSelector(INPUT,'OutputSignals',SIGNALS);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % INPUT can be:
7 | % - an empty cell {}
8 | % - a Matsim block
9 | % SIGNALS is a cell array of strings specifying which signals to
10 | % extract from the inputs bus.
11 | % blk = BusSelector(INPUT,'OutputSignals',SIGNALS,ARGS);
12 | % ARGS is an optional list of parameter/value pairs specifying simulink
13 | % block properties.
14 | %
15 | % Example:
16 | % in1 = Constant('var1');
17 | % in2 = FromWorkspace('var2');
18 | % in1.outport(1,'name','sig1');
19 | % in2.outport(1,'name','sig2');
20 | % buscr = BusCreator({in1,in2},'parent',gcs);
21 | %
22 | % blk = BusSelector(buscr,'OutputSignals',{'sig1'});
23 | %
24 | % See also UNARY_OPERATOR.
25 |
26 | properties
27 |
28 | end
29 |
30 | methods
31 | function this = BusSelector(varargin)
32 | p = inputParser;
33 | p.CaseSensitive = false;
34 | p.KeepUnmatched = true;
35 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
36 | addParamValue(p,'OutputSignals',{},@(x) ischar(x) || iscellstr(x));
37 | parse(p,varargin{:})
38 |
39 | b1 = p.Results.b1;
40 | outputsignals = p.Results.OutputSignals;
41 | args = matsim.helpers.validateArgs(p.Unmatched);
42 |
43 | this = this@matsim.library.unary_operator(b1,'BlockName','Bus Selector',args{:});
44 | if ~iscell(outputsignals), outputsignals = {outputsignals}; end
45 | if ~isempty(outputsignals), this.set('OutputSignals',strjoin(outputsignals,',')); end
46 | end
47 | end
48 |
49 | end
50 |
--------------------------------------------------------------------------------
/+matsim/+library/unary_operator.m:
--------------------------------------------------------------------------------
1 | classdef unary_operator < matsim.library.block
2 | %UNARY_OPERATOR Creates a simulink block with one input.
3 | % Syntax:
4 | % blk = unary_operator('BlockName',OPERATOR);
5 | % OPERATOR is a string. Must match the name (prop: "BlockName") of a block in
6 | % the simulink library.
7 | % blk = unary_operator(INPUT,'BlockName',OPERATOR);
8 | % The block specified as INPUT will be connected to the input port of this block.
9 | % INPUT can be:
10 | % - an empty cell {}
11 | % - a Matsim block
12 | % - a number/numeric array
13 | % blk = unary_operator(INPUT,'BlockName',OPERATOR,ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % input = Constant('var1');
19 | % blk = unary_operator(input,'BlockName','Trigonometric Function');
20 | %
21 | % See also BLOCK.
22 |
23 | properties
24 |
25 | end
26 |
27 | methods
28 | function this = unary_operator(varargin)
29 | p = inputParser;
30 | p.CaseSensitive = false;
31 | p.KeepUnmatched = true;
32 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
33 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
34 | parse(p,varargin{:})
35 |
36 | inputs = {p.Results.b1};
37 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
38 | args = matsim.helpers.validateArgs(p.Unmatched);
39 |
40 | if isempty(parent)
41 | parent = gcs;
42 | end
43 |
44 | this = this@matsim.library.block('parent',parent,args{:});
45 | if matsim.helpers.isArgSpecified(p,'b1')
46 | this.setInputs(inputs);
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/+matsim/+library/Add.m:
--------------------------------------------------------------------------------
1 | classdef Add < matsim.library.block
2 | %Add Creates a simulink Add block.
3 | % Syntax:
4 | % blk = Add(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = Add(INPUTS, ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % in1 = Constant(0);
19 | % in2 = FromWorkspace('var1');
20 | % in3 = FromWorkspace('var2');
21 | % blk1 = Add({in1,in2,in3});
22 | %
23 | % See also BLOCK.
24 |
25 | properties
26 |
27 | end
28 |
29 | methods
30 | function this = Add(varargin)
31 | p = inputParser;
32 | p.CaseSensitive = false;
33 | p.KeepUnmatched = true;
34 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
35 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
36 | parse(p,varargin{:})
37 |
38 | inputs = p.Results.inputs;
39 | if ~iscell(inputs)
40 | inputs = {inputs};
41 | end
42 |
43 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
44 | args = matsim.helpers.validateArgs(p.Unmatched);
45 |
46 | if isempty(parent)
47 | parent = gcs;
48 | end
49 |
50 | this = this@matsim.library.block('BlockName','Add','parent',parent,args{:});
51 |
52 | if matsim.helpers.isArgSpecified(p,'inputs')
53 | this.set('Inputs',repmat('+',1,max(1,length(inputs))))
54 | this.setInputs(inputs);
55 | end
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/+matsim/+library/Merge.m:
--------------------------------------------------------------------------------
1 | classdef Merge < matsim.library.block
2 | %MERGE Creates a simulink Merge block.
3 | % Syntax:
4 | % blk = Merge(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = Merge(INPUTS, ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % in1 = Constant(0);
19 | % in2 = FromWorkspace('var1');
20 | % in3 = FromWorkspace('var2');
21 | % blk = Merge({in1,in2,in3});
22 | %
23 | % See also BLOCK.
24 |
25 | properties
26 |
27 | end
28 |
29 | methods
30 | function this = Merge(varargin)
31 | p = inputParser;
32 | p.CaseSensitive = false;
33 | p.KeepUnmatched = true;
34 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
35 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
36 | parse(p,varargin{:})
37 |
38 | inputs = p.Results.inputs;
39 | if ~iscell(inputs)
40 | inputs = {inputs};
41 | end
42 |
43 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
44 | args = matsim.helpers.validateArgs(p.Unmatched);
45 |
46 | if isempty(parent)
47 | parent = gcs;
48 | end
49 |
50 | this = this@matsim.library.block('BlockType','Merge','parent',parent,args{:});
51 |
52 | if matsim.helpers.isArgSpecified(p,'inputs')
53 | this.set('Inputs',mat2str(max(2,length(inputs))));
54 | this.setInputs(inputs);
55 | end
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/+matsim/+library/Mux.m:
--------------------------------------------------------------------------------
1 | classdef Mux < matsim.library.block
2 | %MUX Creates a simulink Mux block.
3 | % Syntax:
4 | % blk = Mux(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = Mux(INPUTS, ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % in1 = Constant(0);
19 | % in2 = FromWorkspace('var1');
20 | % in3 = FromWorkspace('var2');
21 | % blk1 = Mux({in1,in2,in3});
22 | % blk2 = [in1,in2,in3];
23 | %
24 | % See also BLOCK.
25 |
26 | properties
27 |
28 | end
29 |
30 | methods
31 | function this = Mux(varargin)
32 | p = inputParser;
33 | p.CaseSensitive = false;
34 | p.KeepUnmatched = true;
35 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
36 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
37 | parse(p,varargin{:})
38 |
39 | inputs = p.Results.inputs;
40 | if ~iscell(inputs)
41 | inputs = {inputs};
42 | end
43 |
44 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
45 | args = matsim.helpers.validateArgs(p.Unmatched);
46 |
47 | if isempty(parent)
48 | parent = gcs;
49 | end
50 |
51 | this = this@matsim.library.block('BlockType','Mux','parent',parent,args{:});
52 |
53 | if matsim.helpers.isArgSpecified(p,'inputs')
54 | this.set('Inputs',mat2str(max(1,length(inputs))))
55 | this.setInputs(inputs);
56 | end
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/tools/msim_add_block.m:
--------------------------------------------------------------------------------
1 | function [ blk ] = msim_add_block(dest,varargin)
2 | %MSIM_ADD_BLOCK Add block to simulink model.
3 | % Syntax:
4 | % blk = msim_add_block(PARENT,'BlockName',NAME);
5 | % PARENT is the path in which add the block (e.g. "gcs")
6 | % NAME is the name (prop: "Name") of the block to be created.
7 | % blk = msim_add_block(PARENT,'BlockType',TYPE);
8 | % PARENT is the path in which add the block (e.g. "gcs")
9 | % TYPE is the type (prop: "BlockType") of the block to be created.
10 | % blk = msim_add_block(PARENT,'Library',MODEL,'BlockType',TYPE,ARGS);
11 | % MODEL is the name of the library containing the desired block.
12 | % ARGS is an optional list of parameter/value pairs specifying simulink
13 | % block properties.
14 | %
15 | % Example:
16 | % blk = msim_add_block(gcs,'blocktype','Constant','name','TEST')
17 |
18 | p = inputParser;
19 | p.CaseSensitive = false;
20 | p.KeepUnmatched = true;
21 | addRequired(p,'dest',@(x) get_param(x,'handle'));
22 | addParamValue(p,'Library','simulink',@ischar);
23 | addParamValue(p,'BlockName','',@ischar);
24 | addParamValue(p,'BlockType','',@ischar);
25 | addParamValue(p,'UseMatsim',false,@islogical);
26 | parse(p,dest,varargin{:})
27 |
28 | model = p.Results.Library;
29 | use_matsim = p.Results.UseMatsim;
30 | block_name = p.Results.BlockName;
31 | block_type = p.Results.BlockType;
32 | args = matsim.helpers.validateArgs(p.Unmatched);
33 |
34 | if use_matsim
35 | blk = matsim.library.block('BlockName',block_name,'BlockType',block_type,'Parent',dest,'Model',model,args{:});
36 | else
37 | % Create block
38 | match = matsim.helpers.findBlock(model,'BlockName',block_name,'BlockType',block_type);
39 | if isempty(match)
40 | match = matsim.helpers.findBlock(model,'BlockName',block_name,'BlockType',block_type,'LookUnderMasks','all');
41 | end
42 | if ~isempty(match)
43 | blk = add_block(match{1},strjoin({dest,get_param(match{1},'name')},'/'),'MakeNameUnique','on',args{:});
44 | else
45 | error('Could not find matching block.')
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/+matsim/+library/Delay.m:
--------------------------------------------------------------------------------
1 | classdef Delay < matsim.library.unary_operator
2 | %DELAY Creates a simulink Delay block.
3 | % Syntax:
4 | % blk = Delay(INPUT,'DelayLength',DELAYLENGTH,'x0',X0);
5 | % The block specified as INPUT will be connected to the input port of this block.
6 | % DELAYLENGTH is an integer specifying the number of delay steps. If 1
7 | % a "Unit Delay" block will be used, if greater than 1 the "Delay"
8 | % simulink block will be used.
9 | % X0 is a number that will be set as block's Initial Condition.
10 | %
11 | % Example:
12 | % in1 = FromWorkspace('var1');
13 | % blk = Delay(in1,'DelayLength',2,'x0',0);
14 | %
15 | % See also UNARY_OPERATOR.
16 |
17 | properties
18 |
19 | end
20 |
21 | methods
22 | function this = Delay(varargin)
23 | p = inputParser;
24 | p.CaseSensitive = false;
25 | p.KeepUnmatched = true;
26 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
27 | addParamValue(p,'DelayLength',1,@isnumeric);
28 | addParamValue(p,'SampleTime',-1,@isnumeric);
29 | addParamValue(p,'x0',0,@isnumeric);
30 | parse(p,varargin{:})
31 |
32 | b1 = p.Results.b1;
33 | DelayLength = p.Results.DelayLength;
34 | SampleTime = p.Results.SampleTime;
35 | x0 = p.Results.x0;
36 | args = matsim.helpers.validateArgs(p.Unmatched);
37 |
38 | if DelayLength ~= 1
39 | dl = 'Delay';
40 | else
41 | dl = 'Unit Delay';
42 | end
43 |
44 | this = this@matsim.library.unary_operator(b1,'BlockName',dl,args{:});
45 | this.set('SampleTime',mat2str(SampleTime))
46 | if matsim.utils.getversion() < 2014 && strcmp(dl, 'Unit Delay')
47 | this.set('X0',mat2str(x0))
48 | else
49 | this.set('InitialCondition',mat2str(x0))
50 | end
51 | if DelayLength ~= 1
52 | this.set('DelayLength',mat2str(DelayLength))
53 | end
54 | end
55 | end
56 |
57 | end
58 |
--------------------------------------------------------------------------------
/+matsim/+library/binary_operator.m:
--------------------------------------------------------------------------------
1 | classdef binary_operator < matsim.library.block
2 | %BINARY_OPERATOR Creates a simulink block with two inputs.
3 | % Syntax:
4 | % blk = binary_operator('BlockName',OPERATOR);
5 | % OPERATOR is a string. Must match the name (prop: "BlockName") of a block in
6 | % the simulink library.
7 | % blk = binary_operator(IN1,IN2,'BlockName',OPERATOR);
8 | % The block specified as IN1 will be connected to the first input port of this block.
9 | % The block specified as IN2 will be connected to the second input port of this block.
10 | % IN1 and IN2 can be:
11 | % - an empty cell {}
12 | % - a Matsim block
13 | % - a number/numeric array
14 | % blk = binary_operator(IN1,IN2,'BlockName',OPERATOR,ARGS);
15 | % ARGS is an optional list of parameter/value pairs specifying simulink
16 | % block properties.
17 | %
18 | % Example:
19 | % in1 = Constant('var1');
20 | % blk = binary_operator(in1,-1,'BlockName','2-D Lookup Table');
21 | %
22 | % See also BLOCK.
23 |
24 | properties
25 |
26 | end
27 |
28 | methods
29 | function this = binary_operator(varargin)
30 | p = inputParser;
31 | p.CaseSensitive = true;
32 | p.KeepUnmatched = true;
33 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
34 | addOptional(p,'b2',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
35 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
36 | parse(p,varargin{:})
37 |
38 | inputs = {p.Results.b1,p.Results.b2};
39 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
40 | args = matsim.helpers.validateArgs(p.Unmatched);
41 |
42 | if isempty(parent)
43 | parent = gcs;
44 | end
45 |
46 | this = this@matsim.library.block('parent',parent,args{:});
47 | if matsim.helpers.isArgSpecified(p,'b1')
48 | this.setInputs(inputs);
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/+matsim/+library/Switch.m:
--------------------------------------------------------------------------------
1 | classdef Switch < matsim.library.block
2 | %SWITCH Creates a simulink Switch block.
3 | % Syntax:
4 | % blk = Switch(IN1,COND,IN2);
5 | % IN1 block will be connected to the block first input port.
6 | % COND block will be connected to the block second input port
7 | % (condition port).
8 | % IN2 block will be connected to the block third input port.
9 | % IN1, IN2 and COND can be:
10 | % - an empty cell {}
11 | % - a matsim block
12 | % - a number
13 | % If IN1, IN2 or COND is a number a Constant block with that value will
14 | % be created.
15 | % blk = Switch(IN1,COND,IN2, ARGS);
16 | % ARGS is an optional list of parameter/value pairs specifying simulink
17 | % block properties.
18 | %
19 | % Example:
20 | % in1 = FromWorkspace('var1');
21 | % out = FromWorkspace('var2');
22 | % Switch(1,in1>0,out);
23 | %
24 | % See also BLOCK.
25 |
26 | properties
27 |
28 | end
29 |
30 | methods
31 | function this = Switch(varargin)
32 | p = inputParser;
33 | p.CaseSensitive = false;
34 | p.KeepUnmatched = true;
35 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
36 | addOptional(p,'b2',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
37 | addOptional(p,'cond',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
38 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
39 | parse(p,varargin{:})
40 |
41 | inputs = {p.Results.b1,p.Results.cond,p.Results.b2};
42 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
43 | args = matsim.helpers.validateArgs(p.Unmatched);
44 |
45 | if isempty(parent)
46 | parent = gcs;
47 | end
48 |
49 | this = this@matsim.library.block('BlockType','Switch','parent',parent,args{:});
50 | if matsim.helpers.isArgSpecified(p,'b1')
51 | this.set('Criteria','u2 ~= 0')
52 | this.setInputs(inputs);
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/+matsim/+library/Lookup2D.m:
--------------------------------------------------------------------------------
1 | classdef Lookup2D < matsim.library.binary_operator
2 | %LOOKUP2D Creates a simulink 2-D Lookup Table block.
3 | % Syntax:
4 | % blk = Lookup2D(IN1,IN2,'Table',TABLE,'breakpoints1',BREAKPOINTS1,'breakpoints2',BREAKPOINTS2);
5 | % The blocks specified as IN1 and IN2 will be connected to the input
6 | % ports of this block.
7 | % TABLE is a numeric array or string (variable name) to be set as table
8 | % data.
9 | % BREAKPOINTS1 is a numeric array or string (variable name) to be set as
10 | % table x-data.
11 | % BREAKPOINTS2 is a numeric array or string (variable name) to be set as
12 | % table y-data.
13 | %
14 | % Example:
15 | % in1 = FromWorkspace('var1');
16 | % in2 = FromWorkspace('var2');
17 | % blk = Lookup2D(in1,in2,'Table',rand(3,4),'breakpoints1',[1:3],'breakpoints2',[1:4]);
18 | %
19 | % See also BINARY_OPERATOR.
20 |
21 | properties
22 |
23 | end
24 |
25 | methods
26 | function this = Lookup2D(varargin)
27 | p = inputParser;
28 | p.CaseSensitive = false;
29 | p.KeepUnmatched = true;
30 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
31 | addOptional(p,'b2',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
32 | addParamValue(p,'Table','',@(x) isnumeric(x) || ischar(x));
33 | addParamValue(p,'breakpoints1','',@(x) isnumeric(x) || ischar(x));
34 | addParamValue(p,'breakpoints2','',@(x) isnumeric(x) || ischar(x));
35 | parse(p,varargin{:})
36 |
37 | b1 = p.Results.b1;
38 | b2 = p.Results.b2;
39 | Table = p.Results.Table;
40 | breakpoints1 = p.Results.breakpoints1;
41 | breakpoints2 = p.Results.breakpoints2;
42 | args = matsim.helpers.validateArgs(p.Unmatched);
43 |
44 | this = this@matsim.library.binary_operator(b1,b2,'BlockName','2-D Lookup Table',args{:});
45 | this.set('ExtrapMethod','Clip')
46 | this.set('UseLastTableValue','on')
47 |
48 | this.set('Table',Table)
49 | this.set('BreakpointsForDimension1',breakpoints1)
50 | this.set('BreakpointsForDimension2',breakpoints2)
51 | end
52 | end
53 |
54 | end
55 |
--------------------------------------------------------------------------------
/+matsim/+library/BusCreator.m:
--------------------------------------------------------------------------------
1 | classdef BusCreator < matsim.library.block
2 | %BUSCREATOR Creates a simulink BusCreator block.
3 | % Syntax:
4 | % blk = BusCreator(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = BusCreator(INPUTS, ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % % Create inputs for BusCreator
19 | % in1 = Constant('var1');
20 | % in2 = Constant('var2');
21 | % in3 = FromWorkspace('var3');
22 | %
23 | % % Set signal names
24 | % in1.outport(1,'name','sig1');
25 | % in2.outport(1,'name','sig2');
26 | % in3.outport(1,'name','sig3');
27 | %
28 | % % Create block
29 | % blk = BusCreator({in1,in2,in3},'parent',gcs);
30 | %
31 | % See also BLOCK.
32 |
33 | properties
34 |
35 | end
36 |
37 | methods
38 | function this = BusCreator(varargin)
39 | p = inputParser;
40 | p.CaseSensitive = false;
41 | p.KeepUnmatched = true;
42 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
43 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
44 | parse(p,varargin{:})
45 |
46 | inputs = p.Results.inputs;
47 | if ~iscell(inputs)
48 | inputs = {inputs};
49 | end
50 |
51 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
52 | args = matsim.helpers.validateArgs(p.Unmatched);
53 |
54 | if isempty(parent)
55 | parent = gcs;
56 | end
57 |
58 | this = this@matsim.library.block('BlockType','BusCreator','parent',parent,args{:});
59 |
60 | if matsim.helpers.isArgSpecified(p,'inputs')
61 | this.set('Inputs',mat2str(max(1,length(inputs))))
62 | this.setInputs(inputs);
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/+matsim/+helpers/findBlock.m:
--------------------------------------------------------------------------------
1 | function match = findBlock(sys,varargin)
2 | %FINDBLOCK Find block in simulink system
3 | % match = findBlock(sys,Name,Value)
4 | % Parameters:
5 | % sys, simulink system handle or name
6 | % (optional) BlockName, name of block
7 | % (optional) BlockType, name of block
8 | % (optional) SearchDepth
9 |
10 | % Backup current system
11 | current_system = gcs;
12 |
13 | p = inputParser;
14 | p.CaseSensitive = false;
15 | p.KeepUnmatched = true;
16 | addRequired(p,'sys',@(x) isnumeric(x) && ishandle(x) || ischar(x));
17 | addParamValue(p,'Exact',true,@islogical);
18 | addParamValue(p,'BlockName','',@ischar);
19 | addParamValue(p,'BlockType','',@ischar);
20 | addParamValue(p,'LookUnderMasks','graphical',@ischar);
21 | addParamValue(p,'SearchDepth',-1,@isnumeric);
22 | parse(p,sys,varargin{:})
23 |
24 | sys = p.Results.sys;
25 | search_depth = p.Results.SearchDepth;
26 | block_name = p.Results.BlockName;
27 | block_type = p.Results.BlockType;
28 | exact = p.Results.Exact;
29 | masks = p.Results.LookUnderMasks;
30 | other = matsim.helpers.unpack(p.Unmatched);
31 |
32 | try
33 | load_system(sys);
34 | catch
35 | warning('MATSIM:Build','Could not load %s',get_param(sys,'name'))
36 | end
37 |
38 | if matsim.utils.getversion() >= 2012
39 | args = {'CaseSensitive','off','RegExp','on','LookUnderMasks',masks,'IncludeCommented','on','Type','block'};
40 | else
41 | args = {'CaseSensitive','off','RegExp','on','LookUnderMasks',masks,'Type','block'};
42 | end
43 | if search_depth >= 0
44 | args = ['SearchDepth',mat2str(search_depth),args];
45 | end
46 |
47 | args = [args,other];
48 |
49 | if ~isempty(block_type)
50 | args = [args,'BlockType',['^',escape(block_type),'$']];
51 | end
52 | if ~isempty(block_name)
53 | if exact
54 | args = [args,'name',['^',escape(block_name),'$']];
55 | else
56 | args = [args,'name',escape(block_name)];
57 | end
58 | end
59 |
60 | % Find match in system
61 | match = find_system(sys,args{:});
62 |
63 | % Restore current system
64 | set_param(0,'CurrentSystem',current_system)
65 | end
66 |
67 | function esc = escape(query)
68 | % Escape regex query
69 | esc = regexprep(query,'\[|\]|\(|\)|\*|\+|\?|\.|\|','\\$&');
70 | end
71 |
72 |
--------------------------------------------------------------------------------
/+matsim/+library/MultiPortSwitch.m:
--------------------------------------------------------------------------------
1 | classdef MultiPortSwitch < matsim.library.block
2 | %MULTIPORTSWITCH Creates a simulink MultiPortSwitch block.
3 | % Syntax:
4 | % blk = MultiPortSwitch(IN1,CASES);
5 | % IN1 block will be connected to the block first input port.
6 | % IN1 can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % If IN1 is a number a Constant block with that value will
11 | % be created.
12 | % CASES blocks will be connected to the block other input ports
13 | % CASES can be:
14 | % - an empty cell {}
15 | % - a matsim block
16 | % - a number
17 | % - a cell array of the above
18 | % If CASES is a number a Constant block with that value will
19 | % be created.
20 | %
21 | % Example:
22 | % in1 = FromWorkspace('var1');
23 | % in2 = FromWorkspace('var2');
24 | % in3 = FromWorkspace('var2');
25 | % blk = MultiPortSwitch(in1,{in2,in3});
26 | %
27 | % See also BLOCK.
28 |
29 | properties
30 |
31 | end
32 |
33 | methods
34 | function this = MultiPortSwitch(varargin)
35 | p = inputParser;
36 | p.CaseSensitive = false;
37 | p.KeepUnmatched = true;
38 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
39 | addOptional(p,'cases',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
40 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
41 | parse(p,varargin{:})
42 |
43 | b1 = p.Results.b1;
44 | cases = p.Results.cases;
45 | if ~iscell(cases)
46 | cases = {cases};
47 | end
48 |
49 | inputs = [{b1}, cases];
50 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
51 | args = matsim.helpers.validateArgs(p.Unmatched);
52 |
53 | if isempty(parent)
54 | parent = gcs;
55 | end
56 |
57 | this = this@matsim.library.block('BlockType','MultiPortSwitch','parent',parent,args{:});
58 |
59 | if matsim.helpers.isArgSpecified(p,'b1')
60 | this.setInput(1,'value',b1)
61 | end
62 | if matsim.helpers.isArgSpecified(p,'cases')
63 | this.set('Inputs',mat2str(max(1,length(cases))));
64 | for ii = 1:numel(cases)
65 | this.setInput(1+ii,'value',cases{ii})
66 | end
67 | end
68 | end
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/+matsim/+library/REF.m:
--------------------------------------------------------------------------------
1 | classdef REF < matsim.library.block
2 | %REF Creates a simulink From or Goto block
3 | % Syntax:
4 | % blk = REF('TAG')
5 | % A FROM block with tag 'TAG' will be created.
6 | % blk = REF('TAG',INPUT)
7 | % If INPUT is specified a GOTO block will be created.
8 | % INPUT block will be connected to the block input port.
9 | % INPUT can be:
10 | % - an empty cell {}
11 | % - a matsim block
12 | % - a number
13 | % If INPUT is a number a Constant block with that value will
14 | % be created.
15 | % blk = REF('TAG',ARGS)
16 | % ARGS is an optional list of parameter/value pairs specifying simulink
17 | % block properties.
18 | %
19 | % Example:
20 | % in1 = Constant(1);
21 | % REF('mySig',in1);
22 | % Scope(REF('mySig'));
23 | %
24 | % See also BLOCK.
25 |
26 | properties
27 |
28 | end
29 |
30 | methods
31 | function this = REF(varargin)
32 | p = inputParser;
33 | p.CaseSensitive = false;
34 | p.KeepUnmatched = true;
35 | addRequired(p,'tag',@(x) isnumeric(x) || ischar(x));
36 | addOptional(p,'b1',[],@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
37 | addParamValue(p,'FitSize',true,@(x) islogical(x));
38 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
39 | parse(p,varargin{:})
40 |
41 | inputs = {p.Results.b1};
42 | tag = p.Results.tag;
43 | FitSize = p.Results.FitSize;
44 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
45 | args = matsim.helpers.validateArgs(p.Unmatched);
46 |
47 | if isempty(parent)
48 | parent = gcs;
49 | end
50 |
51 | if isempty(p.Results.b1)
52 | type = 'From';
53 | else
54 | type = 'Goto';
55 | end
56 |
57 | this = this@matsim.library.block('BlockType',type,'parent',parent,args{:});
58 | this.set('ShowName','off');
59 | if matsim.helpers.isArgSpecified(p,'b1')
60 | this.setInputs(inputs);
61 | end
62 |
63 | if isnumeric(tag)
64 | tag = ['ref', mat2str(tag)];
65 | end
66 | this.set('GotoTag',tag);
67 |
68 | if FitSize
69 | location = this.get('Position');
70 | location(3) = location(1)+max(40,10*length(tag));
71 | this.set('Position',location);
72 | end
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/docs/images/logo/generate_logo.m:
--------------------------------------------------------------------------------
1 | clearvars
2 | close all
3 | clc
4 |
5 | % Show text?
6 | show_text = true;
7 |
8 | if show_text
9 | f = figure('Units','norm','Position',[0.05,0.2,0.9,0.6]);
10 | else
11 | f = figure;
12 | end
13 | f.Color = 'white';
14 |
15 | % Create axes for surface
16 | if show_text
17 | ax = axes('Position',[0.22 0 1 1]);
18 | else
19 | ax = axes('Position',[0 0 1 1]);
20 | end
21 | ax.XLim = [1 201];
22 | ax.YLim = [1 201];
23 | ax.ZLim = [-53.4 160];
24 | hold on
25 | axis off
26 | view(3)
27 |
28 | % Create red surface
29 | L = 160*membrane(1,100);
30 | s = surface(L);
31 | s.EdgeColor = 'none';
32 |
33 | % Set camera position
34 | ax.CameraPosition = [-145.5 -229.7 283.6];
35 | ax.CameraTarget = [77.4 60.2 63.9];
36 | ax.CameraUpVector = [0 0 1];
37 | ax.CameraViewAngle = 36.7;
38 | ax.DataAspectRatio = [1 1 .9];
39 |
40 | % Set colormap
41 | length = 100;
42 | red = [1, 0, 0];
43 | pink = [255, 192, 203]/255;
44 | colors_p = [linspace(red(1),pink(1),length)', linspace(red(2),pink(2),length)', linspace(red(3),pink(3),length)'];
45 | colormap(colors_p)
46 |
47 | % Create second axes behind the other
48 | if show_text
49 | ax = axes('Position',[0.22 0 1 1]);
50 | else
51 | ax = axes('Position',[0 0 1 1]);
52 | end
53 | ax.XLim = [-20 201];
54 | ax.YLim = [1 201];
55 | ax.ZLim = [-53.4 160];
56 | uistack(ax,'bottom');
57 | hold on
58 | axis off
59 | view(3)
60 |
61 | % Set camera position
62 | ax.CameraPosition = [35.5 -229.7 183.6];
63 | ax.CameraTarget = [82.4 60.2 63.9];
64 | ax.CameraUpVector = [0 0 1];
65 | ax.CameraViewAngle = 50.7;
66 |
67 | % Plot rectangle
68 | x = [0 100 100 0]-20;
69 | z = [0 0 100 100];
70 | y = [0 0 0 0]+200;
71 | ct = [[226 145 59]/255;[228 95 33]/255;[226 145 59]/255;[226 194 84]/255];
72 | csq(1,1:4,1:3) = ct;
73 | sq = patch(x,y,z,csq);
74 | sq.EdgeColor = 'none';
75 |
76 | % Plot triangle
77 | x = [0 100 50]+20;
78 | z = [0 0 100]+40;
79 | y = [0 0 0]+200;
80 | ct = [[38 86 137]/255;[75 142 187]/255;[89 163 205]/255];
81 | ctr(1,1:3,1:3) = ct;
82 | tr = patch(x,y,z,ctr);
83 | tr.EdgeColor = 'none';
84 |
85 | % Add text
86 | if show_text
87 | ax = axes('Position',[0 0 1 1]);
88 | hold on
89 | axis off
90 | txt = text(0.05,0.5,'Matsim');
91 | set(txt,'FontName','Segoe UI Symbol')
92 | set(txt,'FontSize',140)
93 | set(txt,'Color',[89 89 89]/255)
94 |
95 | % Resize text with figure!!
96 | orig_sz = f.Position;
97 | f.ResizeFcn = @(src,ev) set(txt,'FontSize',src.Position(3)/orig_sz(3)*140);
98 | end
99 |
100 | % Export to PNG
101 | if show_text
102 | export_fig(f,'matsim-icon.png','-m4','-transparent')
103 | else
104 | export_fig(f,'matsim-logo.png','-m4','-transparent')
105 | end
106 |
107 |
--------------------------------------------------------------------------------
/+matsim/+builder/+graphviz/getNeighbours.m:
--------------------------------------------------------------------------------
1 | function neighbours = getNeighbours(sys,root)
2 | %GETNEIGHBOURS Find ancestors of block
3 |
4 | neighbours = [];
5 |
6 | data = get(root,'UserData');
7 | if ~isempty(data) && isfield(data,'block') && ~isempty(data.block)
8 | inputs = data.block.inputs;
9 | if ~isempty(inputs)
10 | % Ordine porte Simulink: [N Inports, 0/1 Enables, 0/1 Triggers, 0/1 Resets, 0/1 IfAction]
11 | in = inputs.inport(cellfun(@(x) isa(x,'matsim.library.block_input'),inputs.inport));
12 | en = inputs.enable(cellfun(@(x) isa(x,'matsim.library.block_input'),inputs.enable));
13 | tr = inputs.trigger(cellfun(@(x) isa(x,'matsim.library.block_input'),inputs.trigger));
14 | rs = inputs.reset(cellfun(@(x) isa(x,'matsim.library.block_input'),inputs.reset));
15 | ia = inputs.ifaction(cellfun(@(x) isa(x,'matsim.library.block_input'),inputs.ifaction));
16 | inputs = [in,en,tr,rs,ia];
17 |
18 | for i=1:length(inputs)
19 | if ~isempty(inputs{i}.value)
20 | in = [inputs{i}.value.get('handle'), inputs{i}.srcport, i];
21 | neighbours = [neighbours; in];
22 | else
23 | in = [-1, inputs{i}.srcport, i];
24 | neighbours = [neighbours; in];
25 | end
26 | end
27 | end
28 | else
29 | ports = matsim.utils.getBlockPorts(root,'input');
30 | for i=1:length(ports)
31 | line = get(ports(i),'line');
32 | if (line == -1 || get(line,'SrcBlockHandle') == -1), continue; end;
33 | neighbours = [neighbours; [get(line,'SrcBlockHandle'), get(get(line,'SrcPortHandle'),'PortNumber'), get(ports(i),'PortNumber')]];
34 | end
35 | end
36 |
37 | % Create edges between gotos and froms so the final graph will place
38 | % them closer to each other
39 | % if strcmp(get(root,'blocktype'),'From')
40 | % GotoTag = get(root, 'Gototag');
41 | % Gotos = matsim.helpers.findBlock(sys,'SearchDepth',1,'BlockType','Goto','Gototag',GotoTag);
42 | % for i = 1:length(Gotos)
43 | % in = [get(Gotos(i),'handle'), -1, -1]; % -1 is implicit connection
44 | % neighbours = [neighbours; in];
45 | % end
46 | % end
47 | % if strcmp(get(root,'blocktype'),'DataStoreMemory')
48 | % DataTag = get(root, 'DataStoreName');
49 | % DataWrite = matsim.helpers.findBlock(sys,'SearchDepth',1,'BlockType','DataStoreWrite','DataStoreName',DataTag);
50 | % for i = 1:length(DataWrite)
51 | % in = [get(DataWrite(i),'handle'), -1, -1]; % -1 is implicit connection
52 | % neighbours = [neighbours; in];
53 | % end
54 | % end
55 |
56 | if ~isempty(neighbours)
57 | % [~,u] = unique(neighbours(:,1),'stable');
58 | % neighbours = neighbours(u,:);
59 | end
60 |
61 | % neighbours = [src_blk, src_port/-1, my_port]
62 | end
63 |
64 |
--------------------------------------------------------------------------------
/+matsim/+library/Scope.m:
--------------------------------------------------------------------------------
1 | classdef Scope < matsim.library.block
2 | %SCOPE Creates a simulink Scope block.
3 | % Syntax:
4 | % blk = Scope(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = Scope(INPUTS, ARGS);
14 | % ARGS is an optional list of parameter/value pairs specifying simulink
15 | % block properties.
16 | %
17 | % Example:
18 | % in1 = Constant(0);
19 | % in2 = FromWorkspace('var1');
20 | % in3 = FromWorkspace('var2');
21 | % blk = Scope({[in1,in2],in3});
22 | %
23 | % Scope Methods:
24 | % open - Open the Scope window
25 | % close - Close the Scope window
26 | %
27 | % See also BLOCK.
28 |
29 | properties
30 |
31 | end
32 |
33 | methods
34 | function this = Scope(varargin)
35 | p = inputParser;
36 | p.CaseSensitive = false;
37 | p.KeepUnmatched = true;
38 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
39 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
40 | parse(p,varargin{:})
41 |
42 | inputs = p.Results.inputs;
43 | if ~iscell(inputs)
44 | inputs = {inputs};
45 | end
46 |
47 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
48 | args = matsim.helpers.validateArgs(p.Unmatched);
49 |
50 | if isempty(parent)
51 | parent = gcs;
52 | end
53 |
54 | this = this@matsim.library.block('BlockType','Scope','parent',parent,args{:});
55 |
56 | if matsim.helpers.isArgSpecified(p,'inputs')
57 | if matsim.utils.getversion() >= 2015
58 | scope_configuration = this.get('ScopeConfiguration');
59 | scope_configuration.NumInputPorts = mat2str(max(1,length(inputs)));
60 | scope_configuration.LayoutDimensions = [max(1,length(inputs)), 1]; % Rows, columns
61 | else
62 | this.set('NumInputPorts',mat2str(max(1,length(inputs))));
63 | end
64 |
65 | this.setInputs(inputs);
66 | end
67 | end
68 |
69 | function [] = open(this)
70 | %OPEN Open the Scope window
71 | % Example:
72 | % blk = Scope({{}});
73 | % blk.open()
74 | set_param(this.handle,'open','on');
75 | end
76 |
77 | function [] = close(this)
78 | %CLOSE Close the Scope window
79 | % Example:
80 | % blk = Scope({{}});
81 | % blk.close()
82 | set_param(this.handle,'open','off');
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/generator/Wizard.m:
--------------------------------------------------------------------------------
1 | clearvars
2 | close all
3 | clc
4 |
5 | blocks = ReadTable();
6 | [~,I] = sort({blocks.name});
7 | blocks = blocks(I);
8 |
9 | selected_block = matsim.utils.handlevar([]);
10 | selected_params = matsim.utils.handlevar([]);
11 |
12 | fig = figure('Units','norm','Position',[0.2 0.2 0.6 0.6],'Toolbar','none');
13 | set(fig,'Resize','off');
14 |
15 | hpanel = uipanel(fig,'Position',[0 0 1 1],'bordertype','none');
16 | hcontent1 = uipanel('Parent',hpanel,'Position',[0 0 0.3 1]);
17 | hcontent2 = uipanel('Parent',hpanel,'Position',[0.3 0 0.7 1]);
18 | bounds1 = getpixelposition(hcontent1);
19 | bounds2 = getpixelposition(hcontent2);
20 |
21 | hsearch = uicontrol('Style','edit','Parent',hcontent1,...
22 | 'FontSize',12,'HorizontalAlignment','left', ...
23 | 'String','',...
24 | 'Units','norm','Position', [0 0.95 1 0.05]);
25 | hlb = uicontrol('Style','listbox','Parent',hcontent1,...
26 | 'FontSize',12,'HorizontalAlignment','left', ...
27 | 'String',{blocks.name},...
28 | 'Units','norm','Position', [0 0 1 0.95]);
29 | hname = uicontrol('Style','text','Parent',hcontent2,...
30 | 'FontSize',12,'HorizontalAlignment','left', ...
31 | 'String','',...
32 | 'Units','norm','Position', [0 0.95 0.8 0.05]);
33 | hgen = uicontrol('Style','pushbutton','Parent',hcontent2,...
34 | 'FontSize',12,'HorizontalAlignment','center', ...
35 | 'String','Generate',...
36 | 'Units','norm','Position', [0.8 0.95 0.2 0.05]);
37 | hparams = uitable('Parent',hcontent2,'FontSize',12,...
38 | 'ColumnName', {'Name','Dialog','Type'},...
39 | 'ColumnWidth', num2cell([bounds2(3)/3,bounds2(3)/3,bounds2(3)/3]-20),...
40 | 'Units','norm','Position', [0 0 1 0.95]);
41 |
42 | set(hsearch,'Callback',{@search,blocks,hlb})
43 | set(hlb,'Callback',{@select_list,blocks,hparams,hname,hsearch,selected_block})
44 | set(hparams,'CellSelectionCallback',{@select_table,selected_params})
45 | set(hgen,'Callback',{@generate,selected_block,selected_params})
46 |
47 | function [] = search(src,~,blocks,hlb)
48 | value = get(src,'String');
49 | if isempty(value)
50 | set(hlb,'String',{blocks.name})
51 | else
52 | blks = blocks(contains(lower({blocks.name}),lower(value)));
53 | set(hlb,'Value',1)
54 | set(hlb,'String',{blks.name})
55 | end
56 | end
57 | function [] = select_list(src,~,blocks,hparams,hname,hsearch,selected_block)
58 | value = get(src,'Value');
59 | if value <= 0, return, end
60 | name = get(hsearch,'String');
61 | if isempty(name)
62 | blks = blocks;
63 | else
64 | blks = blocks(contains(lower({blocks.name}),lower(name)));
65 | end
66 | block = blks(value);
67 | set(hparams,'Data', [{block.params.name}',{block.params.dialog}',{block.params.type}']);
68 | set(hname,'String', sprintf('%s (%s)',block.name,block.type));
69 | selected_block.Value = block;
70 | end
71 | function [] = select_table(~,ev,selected_params)
72 | selected_params.Value = ev.Indices(:,1);
73 | end
74 | function [] = generate(~,~,block,par)
75 | if isempty(block.Value), return, end
76 | GenerateClass(block.Value,par.Value);
77 | end
78 |
79 |
--------------------------------------------------------------------------------
/+matsim/+library/IF.m:
--------------------------------------------------------------------------------
1 | classdef IF < matsim.library.block
2 | %IF Creates a simulink If block.
3 | % Syntax:
4 | % blk = IF(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = IF(INPUTS, 'Expression', EXPRESSION);
14 | % EXPRESSION sets the block "IfExpression" and "ElseIfExpressions".
15 | % EXPRESSION can be:
16 | % - a string
17 | % - a cell array of strings
18 | % First EXPRESSION will be set as the block "IfExpression", the other
19 | % elements will be set as "ElseIfExpressions".
20 | % blk = IF(INPUTS, 'Expression', EXPRESSION, ARGS);
21 | % ARGS is an optional list of parameter/value pairs specifying simulink
22 | % block properties.
23 | %
24 | % Example:
25 | % in1 = FromWorkspace('var1');
26 | % in2 = FromWorkspace('var2');
27 | % blk = IF({in1,in2});
28 | %
29 | % See also BLOCK.
30 |
31 | properties
32 |
33 | end
34 |
35 | methods
36 | function this = IF(varargin)
37 | p = inputParser;
38 | p.CaseSensitive = false;
39 | p.KeepUnmatched = true;
40 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
41 | addParamValue(p,'Expression','',@(x) ischar(x) || iscellstr(x));
42 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
43 | parse(p,varargin{:})
44 |
45 | inputs = p.Results.inputs;
46 | if ~iscell(inputs)
47 | inputs = {inputs};
48 | end
49 |
50 | expression = p.Results.Expression;
51 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
52 | args = matsim.helpers.validateArgs(p.Unmatched);
53 |
54 | if isempty(parent)
55 | parent = gcs;
56 | end
57 |
58 | this = this@matsim.library.block('BlockType','If','parent',parent,args{:});
59 |
60 | if matsim.helpers.isArgSpecified(p,'inputs')
61 | this.set('NumInputs',mat2str(max(1,length(inputs))));
62 | if ~isempty(expression)
63 | if ~iscell(expression), expression = {expression}; end
64 | this.set('IfExpression',expression{1});
65 | if length(expression)>1
66 | this.set('ElseIfExpressions',strjoin(expression(2:end),','));
67 | end
68 | else
69 | this.set('IfExpression','u1>0');
70 | if length(inputs)>1
71 | this.set('ElseIfExpressions',strjoin(arrayfun(@(i) sprintf('u%d>0',i),2:length(inputs),'uni',0),','));
72 | end
73 | end
74 | this.setInputs(inputs);
75 | end
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/+matsim/+builder/+common/emptyRect.m:
--------------------------------------------------------------------------------
1 | function varargout = emptyRect(grid,minw,minh)
2 |
3 | if isempty(grid)
4 | return
5 | end
6 |
7 | [nRows,nColumns] = size(grid);
8 | cache = zeros(1,nColumns);
9 |
10 | bestArea = 0;
11 | if nargin == 3
12 | minArea = calcScore(minw,minh);
13 | else
14 | minArea = 0;
15 | end
16 | bestL = struct('x',0,'y',0);
17 | bestU = struct('x',-1,'y',-1);
18 |
19 | stack = struct('m0',{},'w0',{});
20 | for n = 1:nRows
21 | cache = updateCache(cache,grid,n);
22 | width = 0;
23 | for m = 1:nColumns
24 | if cache(m) > width
25 | stack(end+1) = struct('m0',m,'w0',width);
26 | width = cache(m);
27 | end
28 | if (cache(m) < width)
29 | p = struct('m0',{},'w0',{});
30 | while true
31 | if isempty(stack)
32 | break;
33 | end
34 | p = stack(end);
35 | stack(end) = [];
36 | area = calcScore(width, m - p.m0);
37 | if (area > bestArea)
38 | bestArea = area;
39 | bestL = struct('x',p.m0,'y',n);
40 | bestU = struct('x',m - 1,'y',n - width + 1);
41 | if (bestArea>minArea)
42 | [varargout{1:nargout}] = setReturnValue(bestL,bestU);
43 | return
44 | end
45 | end
46 | width = p.w0;
47 | if cache(m) < width
48 | break
49 | end
50 | end
51 | width = cache(m);
52 | if width ~= 0
53 | stack(end+1) = struct('m0',p.m0,'w0',p.w0);
54 | end
55 | end
56 | end
57 | end
58 | if (bestL.x < 0 || bestL.y < 0 || bestU.x < 0 || bestU.y < 0)
59 | error('Error: no maximal rectangle can be found')
60 | end
61 | if bestArea >= minArea
62 | [varargout{1:nargout}] = setReturnValue(bestL,bestU);
63 | else
64 | [varargout{1:nargout}] = deal([]);
65 | end
66 | end
67 |
68 | function varargout = setReturnValue(bestL,bestU)
69 | if nargout == 1
70 | varargout{1} = struct;
71 | varargout{1}.left = bestL.x;
72 | varargout{1}.right = bestU.x;
73 | varargout{1}.top = bestL.y;
74 | varargout{1}.bottom = bestU.y;
75 | elseif nargout == 4
76 | varargout{1} = bestL.x;
77 | varargout{2} = bestU.x;
78 | varargout{3} = bestL.y;
79 | varargout{4} = bestU.y;
80 | elseif nargout ~= 0
81 | error('Invalid number of output args.')
82 | end
83 | end
84 |
85 | function score = calcScore(width,height)
86 | score = width*height;
87 | end
88 |
89 | function cache = updateCache(cache, grid, n)
90 | [~,nColumns] = size(grid);
91 | for m = 1:nColumns
92 | if (grid(n,m) == false)
93 | cache(m)=cache(m)+1;
94 | else
95 | cache(m)=0;
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/tools/msim_add_line.m:
--------------------------------------------------------------------------------
1 | function hline = msim_add_line(srcs,dests)
2 | %MSIM_ADD_LINE Create line(s) connecting simulink blocks.
3 |
4 | if isempty(srcs) || isempty(dests)
5 | error('Empty source or destination.');
6 | end
7 |
8 | if ~ischar(srcs) && ~isscalar(srcs) && isscalar(dests)
9 | % Many to one
10 | if iscell(srcs)
11 | srcs = cellfun(@(b) get_param(b,'handle'),srcs,'uni',1);
12 | end
13 | if iscell(dests)
14 | dests = cellfun(@(b) get_param(b,'handle'),dests,'uni',1);
15 | end
16 | outports = arrayfun(@(b) matsim.utils.getBlockPorts(b,'output'),srcs,'uni',0);
17 | inports = free_ports(matsim.utils.getBlockPorts(dests,'input'));
18 | outports = free_ports([outports{:}]);
19 | if isempty(inports) || isempty(outports), return, end
20 | locs = get(outports,'position');
21 | if iscell(locs), locs = cell2mat(locs); end
22 | [~,sortIdx] = sort(locs(:,2));
23 | hline = arrayfun(@(idx) add_line(get_param(srcs(1),'parent'),outports(sortIdx(idx)),inports(idx),'AutoRouting','on'),1:min(numel(outports),numel(inports)));
24 | elseif isscalar(srcs) && ~ischar(dests) && ~isscalar(dests)
25 | % One to many
26 | if iscell(srcs)
27 | srcs = cellfun(@(b) get_param(b,'handle'),srcs,'uni',1);
28 | end
29 | if iscell(dests)
30 | dests = cellfun(@(b) get_param(b,'handle'),dests,'uni',1);
31 | end
32 | inports = arrayfun(@(b) matsim.utils.getBlockPorts(b,'input'),dests,'uni',0);
33 | outports = free_ports(matsim.utils.getBlockPorts(srcs,'output'));
34 | inports = free_ports([inports{:}]);
35 | if isempty(inports) || isempty(outports), return, end
36 | locs = get(inports,'position');
37 | if iscell(locs), locs = cell2mat(locs); end
38 | [~,sortIdx] = sort(locs(:,2));
39 | if numel(outports) ~= 1
40 | hline = arrayfun(@(idx) add_line(get_param(srcs(1),'parent'),outports(idx),inports(sortIdx(idx)),'AutoRouting','on'),1:min(numel(inports),numel(outports)));
41 | else
42 | hline = arrayfun(@(idx) add_line(get_param(srcs(1),'parent'),outports(1),inports(sortIdx(idx)),'AutoRouting','on'),1:numel(inports));
43 | end
44 | elseif ~ischar(srcs) && ~isscalar(srcs) && ~ischar(dests) && ~isscalar(dests)
45 | % Many to many
46 | if iscell(srcs)
47 | srcs = cellfun(@(b) get_param(b,'handle'),srcs,'uni',1);
48 | end
49 | if iscell(dests)
50 | dests = cellfun(@(b) get_param(b,'handle'),dests,'uni',1);
51 | end
52 | inports = arrayfun(@(b) matsim.utils.getBlockPorts(b,'input'),dests,'uni',0);
53 | outports = arrayfun(@(b) matsim.utils.getBlockPorts(b,'output'),srcs,'uni',0);
54 | outports = free_ports([outports{:}]);
55 | inports = free_ports([inports{:}]);
56 | if isempty(inports) || isempty(outports), return, end
57 | locs = get(inports,'position');
58 | if iscell(locs), locs = cell2mat(locs); end
59 | [~,sortInportIdx] = sort(locs(:,2));
60 | locs = get(outports,'position');
61 | if iscell(locs), locs = cell2mat(locs); end
62 | [~,sortOutportIdx] = sort(locs(:,2));
63 | hline = arrayfun(@(idx) add_line(get_param(srcs(1),'parent'),outports(sortOutportIdx(idx)),inports(sortInportIdx(idx)),'AutoRouting','on'),1:min(numel(inports),numel(outports)));
64 | else
65 | % One to one
66 | if iscell(srcs)
67 | srcs = cellfun(@(b) get_param(b,'handle'),srcs,'uni',1);
68 | end
69 | if iscell(dests)
70 | dests = cellfun(@(b) get_param(b,'handle'),dests,'uni',1);
71 | end
72 | outports = free_ports(matsim.utils.getBlockPorts(srcs,'output'));
73 | inports = free_ports(matsim.utils.getBlockPorts(dests,'input'));
74 | if isempty(inports) || isempty(outports), return, end
75 | hline = arrayfun(@(idx) add_line(get_param(srcs(1),'parent'),outports(idx),inports(idx),'AutoRouting','on'),1:min(numel(inports),numel(outports)));
76 | end
77 | end
78 |
79 | function free = free_ports(ports)
80 | if numel(ports)>1
81 | free = ports(cell2mat(get(ports,'line'))==-1);
82 | else
83 | free = ports(get(ports,'line')==-1);
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/+matsim/+library/MatlabFunction.m:
--------------------------------------------------------------------------------
1 | classdef MatlabFunction < matsim.library.block
2 | %MATLABFUNCTION Creates a simulink MATLAB Function block.
3 | % Syntax:
4 | % blk = MatlabFunction(INPUTS);
5 | % INPUTS blocks will be connected to the block input ports.
6 | % INPUTS can be:
7 | % - an empty cell {}
8 | % - a matsim block
9 | % - a number
10 | % - a cell array of the above
11 | % If INPUTS is a number a Constant block with that value will
12 | % be created.
13 | % blk = MatlabFunction(INPUTS, 'Script', SCRIPT);
14 | % SCRIPT will be set as MATLAB Function content
15 | % SCRIPT can be:
16 | % - a file name
17 | % - a string (e.g 'function y = fcn(u)')
18 | % If SCRIPT is an existing file name, the contents of that file will be
19 | % read into the block.
20 | % blk = MatlabFunction(INPUTS, 'Script', SCRIPT, ARGS);
21 | % ARGS is an optional list of parameter/value pairs specifying simulink
22 | % block properties.
23 | %
24 | % Example:
25 | % in1 = Constant(0);
26 | % in2 = FromWorkspace('var1');
27 | % blk = MatlabFunction({in1,in2});
28 | % blk.setScript('my_func.m');
29 | %
30 | % MatlabFunction Methods:
31 | % setScript - Set MATLAB Function content
32 | %
33 | % See also BLOCK.
34 |
35 | properties (Access = private)
36 | % Handle to object
37 | chartHandle
38 | end
39 |
40 | methods
41 | function this = MatlabFunction(varargin)
42 | p = inputParser;
43 | p.CaseSensitive = false;
44 | p.KeepUnmatched = true;
45 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
46 | addParamValue(p,'Script','',@(x) ischar(x));
47 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
48 | parse(p,varargin{:})
49 |
50 | inputs = p.Results.inputs;
51 | if ~iscell(inputs)
52 | inputs = {inputs};
53 | end
54 |
55 | Script = p.Results.Script;
56 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
57 | args = matsim.helpers.validateArgs(p.Unmatched);
58 |
59 | if isempty(parent)
60 | parent = gcs;
61 | end
62 |
63 | this = this@matsim.library.block('BlockName','MATLAB Function','parent',parent,args{:});
64 | this.chartHandle = find(slroot, '-isa', 'Stateflow.EMChart', 'Path', matsim.helpers.getBlockPath(this));
65 |
66 | [~,~,ext] = fileparts(Script);
67 | if ~isempty(ext) && exist(Script,'file')==2
68 | this.chartHandle.Script = fileread(Script);
69 | elseif ~isempty(Script)
70 | this.chartHandle.Script = Script;
71 | elseif this.getUserData('created') == 0
72 | this.chartHandle.Script = sprintf('function [y] = fcn(%s)',strjoin(arrayfun(@(i) sprintf('u%d',i),1:length(inputs),'Uni',0),','));
73 | end
74 |
75 | if matsim.helpers.isArgSpecified(p,'inputs')
76 | this.setInputs(inputs);
77 | end
78 | end
79 |
80 | function [] = setScript(this,Script)
81 | %SETSCRIPT Set MATLAB Function content
82 | % Syntax:
83 | % m.setScript(SCRIPT)
84 | % SCRIPT will be set as MATLAB Function content
85 | % SCRIPT can be:
86 | % - a file name
87 | % - a string (e.g 'function y = fcn(u)')
88 | % If SCRIPT is an existing file name, the contents of that file will be
89 | % read into the block.
90 | %
91 | % Example:
92 | % m = MatlabFunction(0);
93 | % m.setScript('my_func.m')
94 | [~,~,ext] = fileparts(Script);
95 | if ~isempty(ext) && exist(Script,'file')==2
96 | this.chartHandle.Script = fileread(Script);
97 | else
98 | this.chartHandle.Script = Script;
99 | end
100 | end
101 | end
102 |
103 | end
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Matsim
7 |
8 |
9 | A sleek, intuitive interface for building Simulink models from a Matlab script.
10 |
11 | Explore Matsim docs »
12 |
13 |
14 | Report bug
15 | ·
16 | Request feature
17 |
18 |
19 |
20 | Matsim is a high level interface to create Simulink models from a [Matlab](https://www.mathworks.com/) script. Matsim is a wrapper around the standard [simulink API](https://it.mathworks.com/help/simulink/ug/approach-modeling-programmatically.html) that makes building a simulink model programmatically much faster.
21 |
22 | [](https://it.mathworks.com/matlabcentral/fileexchange/68436-matsim)  [](https://www.paypal.me/gave92)
23 |
24 |
25 | ## Key features
26 | * **Automatic layout** (no need to specify block positions!)
27 | * **Intuitive interface** (you can "add", "subtract", "multiply", ... simulink blocks)
28 | * **Extensible library** (easily add new blocks from your custom libraries)
29 |
30 | | Source script (.m) | Resulting model | Notes |
31 | :-------------------------:|:-------------------------:|:-------------------------:
32 | `c = Constant(1)` | | Create a Constant block with value 1
33 | `res = a+b` | | Create an Add block and connect its inputs to blocks `a` and `b`
34 | `res = [a,b]` | | Create an Mux block and connect its inputs to blocks `a` and `b`
35 | `res = Max(a,b)` | | Create an MinMax block and connect its inputs to blocks `a` and `b`
36 | `res = 1 - u1./(u2.*u3)` | | Create a group of simulink blocks that computes a complex expression
37 | `Scope(Gain(FromWorkspace('var'),'Gain',0.5))` | | Easily combine blocks
38 |
39 | ## Installation
40 |
41 | The automatic layout feature relies on [GraphViz](https://www.graphviz.org/), which you need to install separately.
42 |
43 | 1. Install [GraphViz](https://www.graphviz.org/download/) and add it to the system PATH
44 | 2. Download and extract the Matsim package (from [File Exhange](https://it.mathworks.com/matlabcentral/fileexchange/68436-matsim) or from here)
45 | 3. Add Matsim folder (and subfolders) to the Matlab path
46 |
47 | ## Quick guide
48 |
49 | Quick example to get started. For more check the [tests](https://github.com/gave92/Matsim/tree/master/tests) folder.
50 |
51 | #### 1. Create or load a simulink model
52 |
53 | ```matlab
54 | import matsim.library.* % Import Matsim package
55 |
56 | sys = simulation.load('my_model'); % Create or load a model named 'my_model'
57 | sys.setSolver('Ts',0.01,'DiscreteOnly',true) % Set solver for the model
58 | sys.clear() % Delete all blocks
59 | sys.show() % Show the model
60 | ```
61 |
62 | #### 2. Create blocks
63 |
64 | ```matlab
65 | Vx = FromWorkspace('V_x'); % Add FromWorkspace and Constant blocks
66 | Wr = FromWorkspace('W_r');
67 | Rr = Constant(0.32);
68 |
69 | slip = 1 - Vx./(Wr.*Rr); % Evaluate complex mathematical expression
70 | sys.log(slip,'name','slip') % Log the output of the "slip" block
71 |
72 | s = Scope(slip); % Create and open scope block
73 | s.open()
74 | ```
75 |
76 | #### 3. Connect and layout the model
77 |
78 | ```matlab
79 | sys.layout() % Connect and layout the model
80 | ```
81 |
82 | #### 4. Simulate the system
83 |
84 | ```matlab
85 | V_x = [0:0.1:10;linspace(5,20,101)]'; % Define input variables
86 | W_r = [0:0.1:10;linspace(5,23,101)/0.32]';
87 | simOut = sys.run('StopTime',10).Logs; % Simulate the system
88 | ```
89 |
90 | © Copyright 2017 - 2019 by Marco Gavelli
91 |
--------------------------------------------------------------------------------
/+matsim/+utils/quicksort.m:
--------------------------------------------------------------------------------
1 | function index = quicksort(input, inl_fcn)
2 |
3 | % QUICKSORT
4 | % syntax:
5 | % Index = quicksort(Input, Cmp_func)
6 | %
7 | % Input may be any Matlab data type multidimensional matrix or cell array.
8 | %
9 | % Input may be a multidimensional matrix or cell array.
10 | % The sort is performed along the first non-singleton
11 | % dimension of Input. The trivial case of a 1x1 input
12 | % will not cause a crash.
13 | %
14 | % Cmp_func may be an inline function or function handle.
15 | % Function handles are much faster than inlines.
16 | % Cmp_func must take as arguments the input array and
17 | % two indices. It then returns 1 if the element(s)
18 | % associated with the first index rank higher than that
19 | % (those) of the second index. It returns -1 if the same
20 | % ranks lower and 0 if they are considered equal.
21 | %
22 | % Index is a 1xN index vector which can be used to sort
23 | % the Input argument.
24 | %
25 | % Example:
26 | % Sort a two-column matrix of integers by pairs with the
27 | % first column in ascending order and the second column
28 | % in descending order when the first column elements are
29 | % equal.
30 | %
31 | % % matrix to be sorted
32 | % x = floor(rand(10000,2)*10);
33 | %
34 | % % inline comparison function
35 | % i_cmp = inline( ...
36 | % 'sign(sign(m(x,1)-m(y,1))*10-sign(m(x,2)-m(y,2)))', ...
37 | % 'm','x','y');
38 | %
39 | % % calculate the sorted index
40 | % order = quicksort(x, i_cmp);
41 | %
42 | % % obtain the sorted matrix
43 | % x_sorted = x(order,:);
44 | %
45 |
46 | sz = size(input);
47 | % while (sz(1) == 1) && (length(sz) > 1)
48 | % sz = sz(2:end);
49 | % end
50 |
51 | index = cumsum(ones(1,sz(1)));
52 |
53 | if isa(inl_fcn, 'function_handle')
54 | index = quicksort_handle(input, inl_fcn, index, 1, sz(1));
55 | else
56 | index = quicksort_inline(input, inl_fcn, index, 1, sz(1));
57 | end
58 |
59 | %
60 | % quicksort with an inline
61 | %
62 | function indx = quicksort_inline(inpt, cmp, indx, l, r)
63 | % l and r remain unchanged. they are the left and right
64 | % bounds of this sorting level
65 | i = l + 1; % leftmost unknown
66 | j = r; % rightmost unknown
67 | p = l; % rightmost equal
68 |
69 | if l >= r
70 | return
71 | end
72 |
73 | % this while loop only runs while p == i - 1
74 | while i <= j
75 | switch cmp(inpt, indx(i), indx(l))
76 | case 1
77 | tmp = indx(j);
78 | indx(j) = indx(i);
79 | indx(i) = tmp;
80 | j = j - 1;
81 | case -1
82 | i = i + 1;
83 | break
84 | otherwise
85 | p = p + 1;
86 | i = i + 1;
87 | end
88 | end
89 |
90 | % this is actually the main while loop
91 | % in this loop i > p + 1
92 | while i <= j
93 | switch cmp(inpt, indx(i), indx(l))
94 | case 1
95 | tmp = indx(j);
96 | indx(j) = indx(i);
97 | indx(i) = tmp;
98 | j = j - 1;
99 | case -1
100 | i = i + 1;
101 | otherwise
102 | p = p + 1;
103 | tmp = indx(p);
104 | indx(p) = indx(i);
105 | indx(i) = tmp;
106 | i = i + 1;
107 | end
108 | end
109 |
110 | % swap "less thans" with "equals"
111 | indx(l:j) = [ indx((p + 1):j) indx(l:p) ];
112 |
113 | indx = quicksort_inline(inpt, cmp, indx, l, l + j - p);
114 | indx = quicksort_inline(inpt, cmp, indx, i, r);
115 |
116 | return;
117 |
118 | %
119 | % quicksort with a function handle
120 | %
121 | function indx = quicksort_handle(inpt, cmp, indx, l, r)
122 | % l and r remain unchanged. they are the left and right
123 | % bounds of this sorting level
124 | i = l + 1; % leftmost unknown
125 | j = r; % rightmost unknown
126 | p = l; % rightmost equal
127 |
128 | if l >= r
129 | return
130 | end
131 |
132 | % this while loop only runs while p == i - 1
133 | while i <= j
134 | switch feval(cmp, inpt, indx(i), indx(l))
135 | case 1
136 | tmp = indx(j);
137 | indx(j) = indx(i);
138 | indx(i) = tmp;
139 | j = j - 1;
140 | case -1
141 | i = i + 1;
142 | break
143 | otherwise
144 | p = p + 1;
145 | i = i + 1;
146 | end
147 | end
148 |
149 | % this is actually the main while loop
150 | % in this loop i > p + 1
151 | while i <= j
152 | switch feval(cmp, inpt, indx(i), indx(l))
153 | case 1
154 | tmp = indx(j);
155 | indx(j) = indx(i);
156 | indx(i) = tmp;
157 | j = j - 1;
158 | case -1
159 | i = i + 1;
160 | otherwise
161 | p = p + 1;
162 | tmp = indx(p);
163 | indx(p) = indx(i);
164 | indx(i) = tmp;
165 | i = i + 1;
166 | end
167 | end
168 |
169 | % swap "less thans" with "equals"
170 | indx(l:j) = [ indx((p + 1):j) indx(l:p) ];
171 |
172 | indx = quicksort_handle(inpt, cmp, indx, l, l + j - p);
173 | indx = quicksort_handle(inpt, cmp, indx, i, r);
174 |
175 | return;
176 |
--------------------------------------------------------------------------------
/generator/GenerateClass.m:
--------------------------------------------------------------------------------
1 | function [fig] = GenerateClass(block,par)
2 |
3 | selected_inputs = matsim.utils.handlevar([]);
4 |
5 | if isempty(par)
6 | params = [];
7 | else
8 | params = block.params(par);
9 | end
10 |
11 | fig = figure('Units','norm','Position',[0.3 0.2 0.4 0.6],'Toolbar','none');
12 | hcontent1 = uipanel('Parent',fig,'Position',[0 0.95 1 0.05],'bordertype','none');
13 | hpanel = uiflowcontainer('v0','Units','norm','Position',[0 0 1 0.95],'FlowDirection','lefttoright','Parent', fig);
14 | hcontent2 = uipanel('Parent',hpanel);
15 | hcontent3 = uipanel('Parent',hpanel);
16 | set(hcontent2,'WidthLimits',[200,200])
17 | bounds1 = getpixelposition(hcontent1);
18 | bounds2 = getpixelposition(hcontent2);
19 | bounds3 = getpixelposition(hcontent3);
20 |
21 | hname = uicontrol('Style','text','Parent',hcontent1,...
22 | 'FontSize',12,'HorizontalAlignment','left', ...
23 | 'String',sprintf('%s (%s)',block.name,block.type),...
24 | 'Units','norm','Position', [0 0 1 1]);
25 | hclass = uicontrol('Style','edit','Parent',hcontent3,...
26 | 'FontSize',12,'HorizontalAlignment','left', ...
27 | 'String','','Max',2,...
28 | 'Units','norm','Position', [0 0 1 1]);
29 |
30 | jScrollPane = findjobj(hclass);
31 | cbStr = sprintf('set(gcbo,''HorizontalScrollBarPolicy'',32)');
32 | hjScrollPane = handle(jScrollPane,'CallbackProperties');
33 | set(hjScrollPane,'ComponentResizedCallback',cbStr);
34 | set(jScrollPane,'HorizontalScrollBarPolicy',32);
35 | jViewPort = jScrollPane.getViewport;
36 | jEditbox = jViewPort.getComponent(0);
37 | jEditbox.setWrapping(false);
38 |
39 | h1 = uicontrol('Style','text','Parent',hcontent2,...
40 | 'FontSize',10,'HorizontalAlignment','left', ...
41 | 'String','Number of inputs',...
42 | 'Units','norm','Position', [0 0.95 1 0.05]);
43 | hinputs = uicontrol('Style','popupmenu','Parent',hcontent2,...
44 | 'FontSize',10,'HorizontalAlignment','left', ...
45 | 'String',{'0','1','2','Any'},...
46 | 'Units','norm','Position', [0 0.9 1 0.05]);
47 | hgo = uicontrol('Style','pushbutton','Parent',hcontent2,...
48 | 'FontSize',10,'HorizontalAlignment','left', ...
49 | 'String','Generate',...
50 | 'Units','norm','Position', [0 0.05 1 0.05]);
51 | hsave = uicontrol('Style','pushbutton','Parent',hcontent2,...
52 | 'FontSize',10,'HorizontalAlignment','left', ...
53 | 'String','Save',...
54 | 'Units','norm','Position', [0 0 1 0.05]);
55 |
56 | set(hinputs,'Callback',{@selected_popup,selected_inputs})
57 | set(hgo,'Callback',{@generate,block,params,hclass,selected_inputs})
58 | set(hsave,'Callback',{@save,block,hclass})
59 | end
60 |
61 | function [] = save(~,~,block,hclass)
62 | s = get(hclass,'String');
63 | txt = '';
64 | for i = 1:size(s,1)
65 | ln = regexprep(s(i,:),'\r|\n','');
66 | ln = regexprep(ln,'[ \t]+$','');
67 | txt = [txt, sprintf('%s\n',ln)];
68 | end
69 |
70 | mkdir('classes')
71 | name = regexprep(block.name,'[^a-zA-z]','');
72 | fileID = fopen(fullfile('classes',[name,'.m']),'w');
73 | fprintf(fileID,'%s',txt);
74 | fclose(fileID);
75 | end
76 | function [] = selected_popup(src,~,selected_popup)
77 | selected_popup.Value = src.Value;
78 | end
79 | function [] = generate(~,~,block,params,hclass,selected_inputs)
80 | num_inputs = selected_inputs.Value;
81 | if isempty(num_inputs), return, end
82 |
83 | name = regexprep(block.name,'[^a-zA-z]','');
84 | paradd = ''; parres = ''; parset = '';
85 | for i = 1:length(params)
86 | parname = regexprep(params(i).name,'[^a-zA-z]','');
87 | paradd = [paradd, repmat(' ',1,12), sprintf('addParamValue(p,''%s'',%s,@(x) ischar(x) || isnumeric(x));\n',parname,'{}')];
88 | parres = [parres, repmat(' ',1,12), sprintf('%s = p.Results.%s;\n',parname,parname)];
89 | parset = [parset, repmat(' ',1,12), sprintf('this.set(''%s'',%s);\n',params(i).name,parname)];
90 | end
91 |
92 | switch num_inputs
93 | case 1
94 | template = fileread('source.txt');
95 | template = sprintf(template,name,upper(name),name,name,name,name,paradd,parres,'simulink',block.name,parset);
96 | case 2
97 | template = fileread('unary.txt');
98 | template = sprintf(template,name,upper(name),name,name,name,name,paradd,parres,block.name,parset);
99 | case 3
100 | template = fileread('binary.txt');
101 | template = sprintf(template,name,upper(name),name,name,name,name,paradd,parres,block.name,parset);
102 | case 4
103 | template = fileread('multiple.txt');
104 | template = sprintf(template,name,upper(name),name,name,name,name,paradd,parres,'simulink',block.name,parset);
105 | end
106 | set(hclass,'String',template)
107 | jhEdit = findjobj(hclass);
108 | jEdit = jhEdit.getComponent(0).getComponent(0);
109 | jEdit.setCaretPosition(0);
110 | end
111 |
112 |
--------------------------------------------------------------------------------
/+matsim/+builder/+common/detectOverlaps.m:
--------------------------------------------------------------------------------
1 | function [overlap_exists, overlaps] = detectOverlaps(baseBlock, otherBlocks, varargin)
2 | % DETECTOVERLAPS Detect whether a block physically overlaps any other block
3 | % in Simulink.
4 | %
5 | % Inputs:
6 | % baseBlock Simulink block. We're checking if any other block
7 | % overlaps this.
8 | % otherBlocks List (cell array or vector) of Simulink blocks (fullnames or
9 | % handles).
10 | % varargin Parameter-Value pairs as detailed below.
11 | %
12 | % Parameter-Value pairs:
13 | % Parameter: 'OverlapType'
14 | % Value: {'Vertical'} - Detects any overlap with respect to top and
15 | % bottom positions (i.e. blocks could be offset on the
16 | % x-axis, but still be deemed overlapping).
17 | % {'Horizontal'} - Detects any overlap with respect to left
18 | % and right positions (i.e. blocks could be offset on the
19 | % y-axis, but still be deemed overlapping).
20 | % {'Any'} - Detects blocks with either a vertical or
21 | % horizontal overlap.
22 | % {'All'} - (Default) Detects blocks sharing space.
23 | % Parameter: 'VirtualBounds'
24 | % Value: Position vector to add to corresponding block dimensions.
25 | % Intended to make near overlaps also count as overlaps. Default:
26 | % [0 0 0 0].
27 | % Parameter: 'PositionFunction'
28 | % Value: Takes a function handle that will be used to determine the
29 | % position of a given block. It may be desirable to use this to
30 | % make near overlaps also count as overlaps. Default is a
31 | % function that just runs get_param(block, 'Position').
32 | %
33 | % Outputs:
34 | % overlap_exists True if any overlaps were detected.
35 | % overlaps Vector of Simulink blocks in otherBlocks that
36 | % overlap baseBlock.
37 |
38 |
39 | % Handle parameter-value pairs
40 | OverlapType = lower('All');
41 | VirtualBounds = [0 0 0 0];
42 | PositionFunction = @getPosition;
43 | for i = 1:2:length(varargin)
44 | param = lower(varargin{i});
45 | value = lower(varargin{i+1});
46 |
47 | switch param
48 | case lower('OverlapType')
49 | assert(any(strcmp(value,lower({'Vertical','Horizontal','Any','All'}))), ...
50 | ['Unexpected value for ' param ' parameter.'])
51 | OverlapType = value;
52 | case lower('VirtualBounds')
53 | assert(length(value) == 4, '''VirtualBounds'' parameter should be a 1x4 vector.')
54 | VirtualBounds = value;
55 | case lower('PositionFunction')
56 | assert(isa(value, 'function_handle'), '''PositionFunction'' parameter should be function handle.')
57 | PositionFunction = value;
58 | otherwise
59 | error('Invalid parameter.')
60 | end
61 | end
62 |
63 | %
64 | otherBlocks = get_param(otherBlocks,'handle');
65 | if iscell(otherBlocks), otherBlocks = cell2mat(otherBlocks); end
66 |
67 | %
68 | overlap_exists = false; % Guess no overlaps
69 | overlaps = zeros(1,length(otherBlocks));
70 | for i = 1:length(otherBlocks)
71 | block = otherBlocks(i);
72 | switch OverlapType
73 | case lower('Vertical')
74 | % Detect vertical overlaps
75 | overlapFound = isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[2,4]); % Check for vertical overlap
76 | case lower('Horizontal')
77 | % Detect horizontal overlaps
78 | overlapFound = isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[1,3]); % Check for vertical overlap
79 | case lower('Any')
80 | % Detect vertical or horizontal overlaps
81 | overlapFound = isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[2,4]) ...
82 | || isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[1,3]);
83 | case lower('All')
84 | % Detect vertical and horizontal overlaps (i.e. both
85 | % occurring at once)
86 | overlapFound = isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[2,4]) ...
87 | && isOverlap(baseBlock,block,VirtualBounds,PositionFunction,[1,3]);
88 | otherwise
89 | error('Unexpected paramter.')
90 | end
91 | if overlapFound
92 | overlap_exists = true;
93 | overlaps(i) = block;
94 | end
95 | end
96 | overlaps = overlaps(find(overlaps)); % Empty elements are non-matches and should be removed
97 | end
98 |
99 | function bool = isOverlap(block1, block2, VirtualBounds, PositionFunction, dims)
100 | %
101 | % dims = [2,4] checks for vertical overlap
102 | % dims = [1,3] checks for horizontal overlap
103 | pos1 = PositionFunction(block1);
104 | pos2 = PositionFunction(block2);
105 |
106 | pos1 = pos1 + VirtualBounds;
107 | pos2 = pos2 + VirtualBounds;
108 |
109 | bool = matsim.builder.common.isRangeOverlap(pos1(dims),pos2(dims));
110 | end
111 |
112 | function pos = getPosition(block)
113 | pos = get_param(block, 'Position');
114 | end
115 |
--------------------------------------------------------------------------------
/+matsim/+builder/+graphviz/genLayout.m:
--------------------------------------------------------------------------------
1 | function obj = genLayout(adjMatrix,blocks)
2 | % Use the graphVIZ package to determine the optimal layout for a graph.
3 |
4 | obj = struct;
5 | obj.blocks = blocks;
6 | obj.adjMatrix = adjMatrix;
7 | obj.layoutFile = 'layout.dot';
8 | obj.adjFile = 'adjmat.dot';
9 |
10 | bounds = [0, 0, 1, 1];
11 | obj.xmin = bounds(1);
12 | obj.ymin = bounds(2);
13 | obj.xmax = bounds(3);
14 | obj.ymax = bounds(4);
15 | obj.maxNodeSize = 1;
16 |
17 | blockSizeRef = getDimensions(blocks);
18 | if ~isempty(blockSizeRef) && ~iscell(blockSizeRef)
19 | blockSizeRef = {blockSizeRef};
20 | end
21 | blockSizeRef = vertcat(blockSizeRef{:});
22 | obj.width = blockSizeRef(:,3) - blockSizeRef(:,1);
23 | obj.height = blockSizeRef(:,4) - blockSizeRef(:,2);
24 |
25 | obj = calcLayout(obj);
26 |
27 | end
28 |
29 | function blockSizeRef = getDimensions(blocks)
30 | blockSizeRef = cell(length(blocks),1);
31 | for i=1:length(blocks)
32 | ports = get(blocks(i),'porthandles');
33 | rot = get(ports.Inport,'Rotation');
34 | if ~iscell(rot), rot = {rot}; end
35 | inputnum = length(find(mod([rot{:}],2*pi) == 0));
36 | rot = get(ports.Outport,'Rotation');
37 | if ~iscell(rot), rot = {rot}; end
38 | outputnum = length(find(mod([rot{:}],2*pi) == 0));
39 | pos = get(blocks(i),'position');
40 | data = get(blocks(i),'UserData');
41 | if ~isempty(data) && isfield(data,'block') && ~isempty(data.block) ...
42 | && ~isempty(data.block.simHeight)
43 | fixedheight = data.block.simHeight;
44 | pos(4) = pos(2)+fixedheight;
45 | elseif max(inputnum,outputnum)>1
46 | pos(4) = pos(2)+42*max([1,inputnum,outputnum]);
47 | end
48 | set(blocks(i),'position',pos);
49 | blockSizeRef{i} = pos;
50 | end
51 | end
52 |
53 | function obj = calcLayout(obj)
54 | % Have graphViz calculate the layout
55 | writeDOTfile(obj);
56 | callGraphViz(obj);
57 | obj = readLayout(obj);
58 | cleanup(obj);
59 | end
60 |
61 | function writeDOTfile(obj)
62 | % Write the adjacency matrix into a dot file that graphViz can
63 | % understand.
64 | fid = fopen('adjmat.dot','w');
65 | try
66 | fprintf(fid,'digraph G {\ncenter=1;\nsize="10,10";\nrankdir="LR";\n');
67 | fprintf(fid,'graph [ranksep=0.4, nodesep=0.4];\n');
68 | n = size(obj.adjMatrix,1);
69 | for i=n:-1:1
70 | width = obj.width(i)*0.5/30;
71 | height = obj.height(i)*0.8/30;
72 | inputnum = length(matsim.utils.getBlockPorts(obj.blocks(i),'input'));
73 | outputnum = length(matsim.utils.getBlockPorts(obj.blocks(i),'output'));
74 | dotfile = [num2str(i),' [label="{'];
75 | if inputnum ~= 0
76 | dotfile = [dotfile '{'];
77 | for x = 1:inputnum
78 | if x == inputnum
79 | dotfile = [dotfile '' num2str(x)];
80 | else
81 | dotfile = [dotfile '' num2str(x) '|'];
82 | end
83 | end
84 | dotfile = [dotfile '}|'];
85 | end
86 | dotfile = [dotfile num2str(i)];
87 | if outputnum ~= 0
88 | dotfile = [dotfile '|{'];
89 | for w = 1:outputnum
90 | if w == outputnum
91 | dotfile = [dotfile '' num2str(w)];
92 | else
93 | dotfile = [dotfile '' num2str(w) '|'];
94 | end
95 | end
96 | dotfile = [dotfile '}'];
97 | end
98 | fprintf(fid,'%s}", shape=record, fixedsize=true, width=%f, height=%f];\n',dotfile,width,height);
99 | end
100 | edgetxt = ' -> ';
101 | for i=n:-1:1
102 | conn = [];
103 | for j=1:n
104 | if(~isempty(obj.adjMatrix{i,j}))
105 | conn = [conn; [i,j,obj.adjMatrix{i,j}]];
106 | end
107 | end
108 | if isempty(conn), continue; end;
109 | ports = matsim.utils.getBlockPorts(obj.blocks(i),'input');
110 | s = matsim.utils.quicksort(conn,@(m,x,y) order_ports(m,x,y,ports));
111 | conn = conn(s,:);
112 | conn(:,5) = 1:size(conn,1);
113 | for j=1:size(conn,1)
114 | if conn(j,4) == -1 % conn(j,4) = -1 is implicit connection (goto->from)
115 | fprintf(fid,'%d%s%d;\n',conn(j,2),edgetxt,i);
116 | else
117 | fprintf(fid,'%d:o%d%s%d:i%d;\n',conn(j,2),conn(j,4),edgetxt,i,conn(j,5));
118 | end
119 | end
120 | end
121 |
122 | fprintf(fid,'}');
123 | catch ex
124 | fclose(fid);
125 | rethrow(ex)
126 | end
127 | fclose(fid);
128 | end
129 |
130 | function comp = order_ports(m,x,y,ports)
131 | ports_order = {'ifaction','reset','trigger','enable','inport'};
132 | if m(x,4) == -1 && m(y,4) == -1
133 | comp = 0;
134 | elseif m(x,4) == -1 && m(y,4) ~= -1
135 | comp = -1;
136 | elseif m(x,4) ~= -1 && m(y,4) == -1
137 | comp = 1;
138 | else
139 | pt1 = ports(m(x,5));
140 | pt2 = ports(m(y,5));
141 | if strcmpi(get(pt1,'porttype'),get(pt2,'porttype'))
142 | comp = sign(get(pt1,'portnumber') - get(pt2,'portnumber'));
143 | else
144 | comp = sign(find(strcmpi(ports_order,get(pt1,'porttype')),1) - find(strcmpi(ports_order,get(pt2,'porttype')),1));
145 | end
146 | end
147 | end
148 |
149 | function callGraphViz(obj)
150 | % Call GraphViz to determine an optimal layout. Write this layout in
151 | % layout.dot for later parsing.
152 | err = system(['dot -Tdot -Gmaxiter=5000 -Gstart=7 -o ',obj.layoutFile,' ',obj.adjFile]);
153 | if(err),error('Sorry, unknown GraphViz failure, try another layout'); end
154 | end
155 |
156 | function obj = readLayout(obj)
157 | % Parse the layout.dot file for the graphViz node locations and
158 | % dimensions.
159 | fid = fopen(obj.layoutFile,'r');
160 | text = textscan(fid,'%s','delimiter','\n');
161 | fclose(fid);
162 | text = text{:};
163 |
164 | % Read Graphviz dimensions
165 | dim_text = text{strncmp(text, 'graph [bb="', 11)};
166 | dims = sscanf(dim_text(12:end),'%f,')';
167 |
168 | % Read the position of nodes
169 | location_tokens = regexp(horzcat(text{:}), ';([0-9]+)\s+\[[^\]]*pos="(-?[0-9\.]+),(-?[0-9\.]+)"', 'tokens');
170 | location_tokens = vertcat(location_tokens{:});
171 |
172 | % Convert to numeric values
173 | node_idx = sscanf(strjoin(location_tokens(:,1)), '%d');
174 | locations = reshape(sscanf(strjoin(location_tokens(:,2:3)), '%f'),[],2);
175 | locations(node_idx,:) = locations; % reorder based on id
176 |
177 | locations(:,2) = dims(4)-locations(:,2);
178 | obj.centers = locations;
179 | end
180 |
181 | function cleanup(obj)
182 | % delete the temporary files.
183 | % !dot -Tpng adjmat.dot -o graph.png
184 | delete(obj.adjFile);
185 | delete(obj.layoutFile);
186 | end
187 |
188 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Matsim  [](https://www.paypal.me/gave92)
4 |
5 | > A sleek, intuitive interface for building Simulink models from a Matlab script.
6 |
7 | ## What is it
8 |
9 | Matsim is a high level interface to create Simulink models from a [Matlab](https://www.mathworks.com/) script. Matsim is a wrapper around the standard [simulink API](https://it.mathworks.com/help/simulink/ug/approach-modeling-programmatically.html) that makes building a simulink model programmatically much faster.
10 |
11 | ## Key features
12 | * **Automatic layout** (no need to specify block positions!)
13 | * **Intuitive interface** (you can "add", "subtract", "multiply", ... simulink blocks)
14 | * **Extensible library** (easily add new blocks from your custom libraries)
15 |
16 | ## Matsim vs Simulink API
17 |
18 |
19 |
20 | With Matsim
21 | With simulink API
22 |
23 |
24 | Create or load model
25 |
26 | sys = simulation. load ( 'my_model' ) ;
27 | sys. show ( )
28 |
29 |
30 | sys = new_system ( 'my_model' ) ;
31 | open_system ( sys) ;
32 |
33 |
34 |
35 | Add blocks
36 |
37 | Vx = FromWorkspace ( 'V_x' ) ;
38 | c = Constant ( 1 ) ;
39 | res = Vx+ c;
40 |
41 |
42 | add_block ( 'simulink/Sources/From Workspace' , 'my_model/From Workspace' ) ;
43 | set_param ( 'my_model/From Workspace' , 'VariableName' , 'V_x' )
44 | add_block ( 'simulink/Sources/Constant' , 'my_model/Constant' ) ;
45 | set_param ( 'my_model/Constant' , 'Value' , '1' )
46 | add_block ( 'simulink/Math Operations/Add' , 'my_model/Add' ) ;
47 |
48 |
49 |
50 | Layout and connect
51 |
52 | sys. layout ( ) ;
53 |
54 |
55 | set_param ( 'my_model/From Workspace' , 'Position' , '[30, 13, 95, 37]' )
56 | set_param ( 'my_model/Constant' , 'Position' , '[45, 90, 75, 120]' )
57 | set_param ( 'my_model/Add' , 'Position' , '[170, 47, 200, 78]' )
58 | add_line ( 'my_model' , 'From Workspace/1' , 'Add/1' , 'autorouting' , 'on' ) ;
59 | add_line ( 'my_model' , 'Constant/1' , 'Add/2' , 'autorouting' , 'on' ) ;
60 |
61 |
62 |
63 |
64 | ## Quick start
65 |
66 | Jump right in with the [installation guide](quickstart.md#installation) and the [examples](quickstart.md#examples)!
67 |
68 | © Copyright 2017 - 2018 by Marco Gavelli
69 |
--------------------------------------------------------------------------------
/docs/quickstart.md:
--------------------------------------------------------------------------------
1 | # Getting started
2 |
3 | ## Installation
4 |
5 | The automatic layout feature relies on [GraphViz](https://www.graphviz.org/), which you need to install separately.
6 |
7 | 1. Install [GraphViz](https://www.graphviz.org/download/) and add it to the system PATH
8 | 2. Download and extract the Matsim package (from [File Exhange](https://it.mathworks.com/matlabcentral/fileexchange/68436-matsim) or from [Github](https://github.com/gave92/Matsim/archive/master.zip))
9 | 3. Add Matsim folder (and subfolders) to the Matlab path
10 |
11 | ## Concepts
12 |
13 | ### Block creation
14 | In Matsim each simulink block is wrapped in a Matlab class. Every time you create an instance of a class, the corresponding simulink block is added to the model.
15 |
16 | For example, if you write:
17 | ```matlab
18 | c = Constant(1);
19 | ```
20 | a simulink **Constant** block is added to the model.
21 |
22 | All Matsim classes derive from the base **block** class, so you can also write:
23 | ```matlab
24 | c = block('model','simulink','type','Constant');
25 | set(c,'Value','1')
26 | ```
27 | where the **'model'** and **'type'** parameters specify what kind of simulink block to create (i.e. the block named 'Constant' from the model library 'simulink'). This is more verbose but allows you a greater flexibility.
28 |
29 | For many of the most common simulink blocks, Matsim provides a tailored wrapper class as a shortcut to easily create that kind of block. For example the **FromWorkspace** class constructor accepts a parameter that sets the blocks 'VariableName' parameter.
30 | ```matlab
31 | fw = FromWorkspace('var1');
32 | ```
33 | In plain simulink API this would be equivalent to:
34 | ```matlab
35 | fw = add_block('simulink/Sources/From Workspace','my_model/From Workspace');
36 | set_param(fw,'VariableName','var1')
37 | ```
38 |
39 | All standard simulink block properties are still available when using Matsim. So to change the block color you would do:
40 | ```matlab
41 | fw = FromWorkspace('var1');
42 | set(fw,'ForegroundColor','red');
43 | ```
44 | Or directly:
45 | ```matlab
46 | fw = FromWorkspace('var1','ForegroundColor','red');
47 | ```
48 |
49 | ### Block connection
50 | In Matsim it's very easy to quickly connect blocks. When you create a new block, you can also specify the inputs to that block.
51 |
52 |
53 |
54 |
55 |
56 | c1 = Constant ( 1 , 'name' , 'c1' ) ;
57 | c2 = Constant ( 'var1' , 'name' , 'c2' ) ;
58 |
59 |
60 | res = Max ( c1, c2, 'name' , 'result' ) ;
61 |
62 | →
63 |
64 |
65 |
66 |
67 |
68 | A block can become an input for the next.
69 |
70 |
71 |
72 |
73 | Scope ( Gain ( FromWorkspace ( 'var' ) , 'Gain' , 0.5 ) )
74 |
75 | →
76 |
77 |
78 |
79 |
80 |
81 |
82 | Matsim translates common operations to equivalent simulink blocks. So you can "Add (+)", "Subtract (-)", "Multiply (.*)", "Concatenate ([])", ... blocks, and Matsim will generate the equivalent model. "a+b" will be translated as an "Add" block, "[a,b]" as a Mux block and so on.
83 |
84 |
85 |
86 |
87 |
88 | u1 = FromWorkspace ( 'u1' ) ;
89 | u2 = FromWorkspace ( 'u2' ) ;
90 | u3 = FromWorkspace ( 'u3' ) ;
91 |
92 |
93 | res = 1 - u1./ ( u2.* u3)
94 |
95 | →
96 |
97 |
98 |
99 |
100 |
101 |
102 | ### Model layout
103 | With Matsim there's no need to specify the blocks positions on the simulink model! Matsim uses GraphViz to automatically generate a block layout. To position and connect the blocks already in the model, call `sys.layout()`:
104 |
105 | ```matlab
106 | import matsim.library.*
107 |
108 | sys = load_system('my_model');
109 | ...
110 | % Create blocks
111 | ...
112 | % Connect and layout the model
113 | sys.layout()
114 | ```
115 |
116 | ## Examples
117 |
118 | Quick example to get started. For more check the [tests](https://github.com/gave92/Matsim/tree/master/tests) folder.
119 |
120 | #### 1. Create or load a simulink model
121 |
122 | ```matlab
123 | import matsim.library.* % Import Matsim package
124 |
125 | sys = simulation.load('my_model'); % Create or load a model named 'my_model'
126 | sys.setSolver('Ts',0.01,'DiscreteOnly',true) % Set solver for the model
127 | sys.clear() % Delete all blocks
128 | sys.show() % Show the model
129 | ```
130 |
131 | #### 2. Create blocks
132 |
133 | ```matlab
134 | Vx = FromWorkspace('V_x'); % Add FromWorkspace and Constant blocks
135 | Wr = FromWorkspace('W_r');
136 | Rr = Constant(0.32);
137 |
138 | slip = 1 - Vx./(Wr.*Rr); % Evaluate complex mathematical expression
139 | sys.log(slip,'name','slip') % Log the output of the "slip" block
140 |
141 | s = Scope(slip); % Create and open scope block
142 | s.open()
143 | ```
144 |
145 | #### 3. Connect and layout the model
146 |
147 | ```matlab
148 | sys.layout() % Connect and layout the model
149 | ```
150 |
--------------------------------------------------------------------------------
/+matsim/+library/simulation.m:
--------------------------------------------------------------------------------
1 | classdef simulation < handle
2 | %SIMULATION Creates or loads a simulink block diagram.
3 | % Syntax:
4 | % sys = simulation.load(MODEL);
5 | % MODEL is the name of the simulink model to be loaded. If MODEL does
6 | % not exist, a new simulink model will be created.
7 | %
8 | % Simulation Methods:
9 | % setSolver - set solver for this model
10 | % show - show the model window
11 | % save - save the model
12 | % close - close the model (without saving)
13 | % clear - deletes all model content
14 | % layout - layout and connect blocks in the model
15 | % open - navigate to subsystem
16 | % run - run simulation
17 | % log - log a signal
18 | %
19 | % Example:
20 | % sys = simulation.load('my_model'); % Create or load a model named 'my_model'
21 | % sys.setSolver('Ts',0.01,'DiscreteOnly',true) % Set solver for the model
22 | % sys.clear() % Delete all blocks
23 | % sys.show() % Show the model
24 | % Scope(Gain(FromWorkspace('var1'),'Gain',0.5)) % Add blocks to the model
25 | % sys.layout() % Connect and layout the model
26 | % sys.save()
27 | % sys.close()
28 |
29 | properties (Access = private)
30 | % Handle to simulink system
31 | simDiagram
32 | end
33 |
34 | methods (Static)
35 | function sim = new(name)
36 | %NEW Creates a new simulink block diagram.
37 | % Syntax:
38 | % sys = simulation.new(MODEL);
39 | % MODEL is the name of the simulink model to be created. If
40 | % MODEL already exists an erro is thrown.
41 |
42 | sys = new_system(name,'ErrorIfShadowed');
43 | sim = matsim.library.simulation(sys);
44 | set_param(0,'CurrentSystem',sys)
45 | end
46 |
47 | function sim = load(name)
48 | %LOAD Creates or loads a simulink block diagram.
49 | % Syntax:
50 | % sys = simulation.load(MODEL);
51 | % MODEL is the name of the simulink model to be loaded. If MODEL does
52 | % not exist, a new simulink model will be created.
53 |
54 | try
55 | sys = load_system(name);
56 | sim = matsim.library.simulation(sys);
57 | set_param(0,'CurrentSystem',sys)
58 | catch
59 | sim = matsim.library.simulation.new(name);
60 | end
61 | end
62 | end
63 |
64 | methods
65 | function this = simulation(varargin)
66 | p = inputParser;
67 | p.CaseSensitive = false;
68 | p.KeepUnmatched = true;
69 | addRequired(p,'sys',@ishandle);
70 | parse(p,varargin{:})
71 |
72 | sys = p.Results.sys;
73 | this.simDiagram = sys;
74 |
75 | % Set output and logging format
76 | set_param(this.handle,'SaveFormat','Array')
77 | set_param(this.handle,'SignalLoggingSaveFormat','Dataset')
78 | end
79 |
80 | function [] = setSolver(this,varargin)
81 | %SETSOLVER Set solver options for this model
82 | % Syntax:
83 | % sys.setSolver('Ts',TS);
84 | % Sets solver sample time. If TS~=0 a fixed step solver
85 | % will be set. Otherwise "VariableStepAuto" selver will be
86 | % used.
87 | % sys.setSolver('Ts',TS,'DiscreteOnly',DISCRONLY);
88 | % If DISCRONLY is true, "FixedStepDiscrete" solver will be
89 | % used. Otherwise "FixedStepAuto" solver is set.
90 |
91 | p = inputParser;
92 | p.CaseSensitive = false;
93 | p.KeepUnmatched = true;
94 | addParamValue(p,'DiscreteOnly',false,@islogical);
95 | addParamValue(p,'Ts',0,@isnumeric);
96 | parse(p,varargin{:})
97 |
98 | Ts = p.Results.Ts;
99 | DiscreteOnly = p.Results.DiscreteOnly;
100 |
101 | if Ts ~= 0
102 | this.set('FixedStep',mat2str(Ts))
103 | if DiscreteOnly
104 | this.set('SolverName','FixedStepDiscrete')
105 | else
106 | this.set('SolverName','FixedStepAuto')
107 | end
108 | else
109 | this.set('SolverName','VariableStepAuto')
110 | end
111 | end
112 |
113 | function [] = show(this)
114 | %SHOW Shows the model window
115 |
116 | if strcmp(this.get('Shown'),'off') ...
117 | || ~strcmp(bdroot, this.get('name'))
118 | open_system(this.handle)
119 | end
120 | end
121 |
122 | function [] = save(this,varargin)
123 | %SAVE Save the model to file
124 | % Syntax:
125 | % sys.save();
126 | % Save the model in the current folder.
127 | % sys.save('path',PATH);
128 | % Save the model in the specified path.
129 |
130 | p = inputParser;
131 | p.CaseSensitive = false;
132 | p.KeepUnmatched = true;
133 | addOptional(p,'path',[],@ischar);
134 | parse(p,varargin{:})
135 |
136 | args = matsim.helpers.validateArgs(p.Unmatched);
137 | save_system(this.handle,fullfile(p.Results.path,this.get('name')),args{:})
138 | end
139 |
140 | function [] = close(this)
141 | %CLOSE Close the model without saving
142 |
143 | if strcmp(this.get('Shown'),'off') ...
144 | || strcmp(this.get('Dirty'),'off')
145 | close_system(this.handle,0)
146 | end
147 | end
148 |
149 | function [] = clear(this)
150 | %CLEAR Deletes all model content
151 |
152 | if strcmp(this.get('Shown'),'on') ...
153 | && ~strcmp(gcs, this.get('name'))
154 | open_system(this.handle)
155 | end
156 | Simulink.BlockDiagram.deleteContents(this.handle)
157 | end
158 |
159 | function [] = layout(this)
160 | %LAYOUT Layout and connect blocks in the model
161 |
162 | matsim.builder.graphviz.simlayout(this.handle,'Recursive',true)
163 | end
164 |
165 | function [] = export(this)
166 | %EXPORT Removes all Matsim info from the model
167 |
168 | blocks = find_system(this.handle,'type','block');
169 | for i=1:length(blocks)
170 | data = get(blocks(i),'UserData');
171 | if isfield(data,'block'), data = rmfield(data,'block'); end
172 | if isfield(data,'created'), data = rmfield(data,'created'); end
173 | set(blocks(i),'UserData',data);
174 | end
175 | end
176 |
177 | function [] = open(this,varargin)
178 | %OPEN Navigate to specified subsystem
179 | % Syntax:
180 | % sys.open(PATH);
181 | % Navigate to the selected path in the model
182 | % sys.open(PATH,'show',SHOW);
183 | % If SHOW is true open the subsystem in a new tab. If false
184 | % only set the "CurrentSystem" property.
185 | %
186 | % Example:
187 | % sys = simulation.load('my_model')
188 | % sys.show()
189 | % s = Subsystem('name','TEST');
190 | % sys.open('my_model/TEST','show',true)
191 |
192 | p = inputParser;
193 | p.CaseSensitive = false;
194 | p.KeepUnmatched = true;
195 | addOptional(p,'subsystem',{},@(x) isempty(x) || ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
196 | addParamValue(p,'show',false,@islogical)
197 | parse(p,varargin{:})
198 |
199 | sys = p.Results.subsystem;
200 | if isempty(sys)
201 | sys = this;
202 | end
203 |
204 | subsystem = matsim.helpers.getBlockPath(sys);
205 | if ~strcmp(gcs, subsystem)
206 | if p.Results.show
207 | open_system(subsystem,'tab')
208 | else
209 | set_param(0,'CurrentSystem',subsystem)
210 | end
211 | end
212 | end
213 |
214 | function simOut = run(this,varargin)
215 | %RUN Run simulation and return results
216 | % Syntax:
217 | % results = sys.run(ARGS);
218 | % ARGS are passed to simulink "sim" command
219 | %
220 | % Example:
221 | % sys = simulation.load('my_model')
222 | % simOut = sys.run('StartTime',0,'StopTime',10);
223 |
224 | p = inputParser;
225 | p.CaseSensitive = false;
226 | p.KeepUnmatched = true;
227 | parse(p,varargin{:})
228 |
229 | args = matsim.helpers.validateArgs(p.Unmatched);
230 | simOut = matsim.library.simOutput(sim(this.get('name'),args{:}));
231 | end
232 |
233 | function [] = log(this,varargin)
234 | %LOG Log a signal
235 | % Syntax:
236 | % results = sys.log(BLOCK);
237 | % Logs first outport of BLOCK. Line label is used as signal name.
238 | % results = sys.log(BLOCK,'name',MYSIGNAL);
239 | % Logs first outport of BLOCK. MYSIGNAL is used as signal name.
240 | % results = sys.log(BLOCK,'port',PORT);
241 | % Logs PORT outport of BLOCK. PORT is integer.
242 | %
243 | % Example:
244 | % sys = simulation.load('my_model')
245 | % sys.show()
246 | % blk = Gain(Constant(1));
247 | % blk.outport(1,'name','test_signal');
248 | % Terminator(blk);
249 | % sys.layout()
250 | % sys.log(blk)
251 | % sys.log(blk,'name','custom_name')
252 |
253 | p = inputParser;
254 | p.CaseSensitive = false;
255 | p.KeepUnmatched = true;
256 | addRequired(p,'block',@(x) isa(x,'matsim.library.block'))
257 | addParamValue(p,'name','',@ischar)
258 | addParamValue(p,'port',1,@isnumeric);
259 | parse(p,varargin{:})
260 |
261 | block = p.Results.block;
262 | name = p.Results.name;
263 | port = p.Results.port;
264 |
265 | ph = get(block,'porthandles');
266 | if ~isempty(name)
267 | set(ph.Outport(port),'DataLogging','on');
268 | set(ph.Outport(port),'DataLoggingNameMode','Custom');
269 | set(ph.Outport(port),'DataLoggingName',name);
270 | elseif ~isempty(get(ph.Outport(port),'SignalNameFromLabel'))
271 | set(ph.Outport(port),'DataLogging','on');
272 | set(ph.Outport(port),'DataLoggingNameMode','SignalName');
273 | set(ph.Outport(port),'DataLoggingName',get(ph.Outport(port),'SignalNameFromLabel'));
274 | elseif ~isempty(get(ph.Outport(port),'PropagatedSignals'))
275 | set(ph.Outport(port),'DataLogging','on');
276 | set(ph.Outport(port),'DataLoggingNameMode','Custom');
277 | set(ph.Outport(port),'DataLoggingName',get(ph.Outport(port),'PropagatedSignals'));
278 | else
279 | set(ph.Outport(port),'DataLogging','off');
280 | warning('MATSIM:Simulation','Cannot log this line. Specify the ''name'' parameter.')
281 | end
282 | end
283 |
284 | function h = handle(this)
285 | h = this.simDiagram;
286 | end
287 | function p = get(this,prop)
288 | p = get(this.simDiagram,prop);
289 | end
290 | function [] = set(this,prop,value)
291 | if iscell(prop)
292 | arrayfun(@(i) this.set(prop{i},prop{i+1}), 1:2:length(prop)-1)
293 | return
294 | end
295 |
296 | set(this.simDiagram,prop,value);
297 | end
298 | end
299 |
300 | end
301 |
--------------------------------------------------------------------------------
/+matsim/+builder/+graphviz/simlayout.m:
--------------------------------------------------------------------------------
1 | function [] = simlayout(varargin)
2 | %SIMLAYOUT Layouts a simulink model
3 |
4 | p = inputParser;
5 | p.CaseSensitive = false;
6 | p.KeepUnmatched = true;
7 | addRequired(p,'sys',@ishandle);
8 | addParamValue(p,'Recursive',false,@islogical);
9 | addParamValue(p,'Blocks',[],@(x) all(ishandle(x)) || (iscell(x) && all(cellfun(@(b) ischar(b) || isa(b,'matsim.library.block') || ishandle(b),x))));
10 | parse(p,varargin{:})
11 |
12 | sys = p.Results.sys;
13 | recursive = p.Results.Recursive;
14 |
15 | % Only layout specified blocks
16 | blocksToLayout = p.Results.Blocks;
17 | if ~isempty(blocksToLayout)
18 | if iscell(blocksToLayout)
19 | blocksToLayout = cellfun(@(b) get_param(b,'handle'),blocksToLayout);
20 | else
21 | blocksToLayout = get_param(blocksToLayout,'handle');
22 | if iscell(blocksToLayout), blocksToLayout = cell2mat(blocksToLayout); end
23 | end
24 | end
25 |
26 | % Build adjacency matrix
27 | [adjMatrix, blocks] = matsim.builder.graphviz.sim2adj(sys,blocksToLayout);
28 | if isempty(blocks), return, end
29 |
30 | % Call graphviz
31 | layout = matsim.builder.graphviz.genLayout(adjMatrix,blocks);
32 | locs = layout.centers;
33 |
34 | % Recursively set blocks position
35 | for i=1:length(blocks)
36 | % Get more information for position calculation
37 | blockSizeRef = get(blocks(i),'Position');
38 |
39 | width = blockSizeRef(3) - blockSizeRef(1);
40 | height = blockSizeRef(4) - blockSizeRef(2);
41 | p_X = locs(i,1);
42 | p_Y = locs(i,2);
43 | location = [p_X-ceil(width/2) p_Y-ceil(height/2) p_X+floor(width/2) p_Y+floor(height/2)];
44 | set(blocks(i),'position',location)
45 |
46 | % Layout subsystem, if not a chart
47 | % 2011: ~strcmp(get(blocks(i),'MaskType'),'Stateflow')
48 | if recursive && strcmp(get(blocks(i),'blocktype'),'SubSystem') && ...
49 | strcmp(get(blocks(i),'SFBlockType'),'NONE')
50 | matsim.builder.graphviz.simlayout(blocks(i))
51 | set(blocks(i),'ZoomFactor','FitSystem')
52 | end
53 | end
54 |
55 | layout = matsim.utils.handlevar(layout);
56 | % Get blocks rank
57 | getRank(layout);
58 |
59 | % Try align blocks
60 | passes = 1;
61 | tryAlignBlocksBackward(layout);
62 | while layout.Value.dirty
63 | if passes>5
64 | warning('MATSIM:Layout','Loop detected: stopping layout.');
65 | break
66 | end
67 | tryAlignBlocksBackward(layout);
68 | passes = passes+1;
69 | end
70 |
71 | % Try align roots
72 | passes = 1;
73 | tryAlignBlocksForward(layout);
74 | while layout.Value.dirty
75 | if passes>5
76 | warning('MATSIM:Layout','Loop detected: stopping layout.');
77 | break
78 | end
79 | tryAlignBlocksForward(layout);
80 | passes = passes+1;
81 | end
82 |
83 | % Move to empty place
84 | if ~isempty(blocksToLayout)
85 | tryMoveBlocks(layout,sys);
86 | end
87 |
88 | % Create lines
89 | for i=1:length(blocks)
90 | ports = matsim.utils.getBlockPorts(blocks(i),'input');
91 | parents = matsim.builder.graphviz.getNeighbours(sys,blocks(i));
92 | port_num = get(ports,'portnumber');
93 | if iscell(port_num)
94 | port_num = cell2mat(port_num);
95 | end
96 | line = get(ports,'line');
97 | if ~iscell(line), line={line}; end
98 | delete_line(cell2mat(line(cell2mat(line)~=-1)));
99 |
100 | for p = 1:size(parents,1)
101 | onum = parents(p,2);
102 | inum = parents(p,3);
103 |
104 | if parents(p,1) ~= -1 && onum ~= -1 % onum = -1 is implicit connection (goto->from)
105 | h1 = get(parents(p,1),'PortHandles');
106 | add_line(get(blocks(i),'parent'),h1.Outport(onum),ports(port_num==inum),'autorouting','on');
107 | end
108 | end
109 | end
110 |
111 | % Delete spacer blocks
112 | for i=1:length(blocks)
113 | data = get(blocks(i),'UserData');
114 | if ~isempty(data) && isfield(data,'block') && ~isempty(data.block) ...
115 | && ~isempty(data.block.simIsVirtual) ...
116 | && data.block.simIsVirtual
117 | inport = matsim.utils.getBlockPorts(blocks(i),'input');
118 | outport = matsim.utils.getBlockPorts(blocks(i),'output');
119 | if numel(inport)~=1 || numel(outport)~=1
120 | continue
121 | end
122 | line_in = get(inport,'line');
123 | line_out = get(outport,'line');
124 | if line_in==-1 || line_out==-1
125 | continue
126 | end
127 | line_in_pts = get(line_in,'points');
128 | line_out_pts = get(line_out,'points');
129 | parent = get(blocks(i),'parent');
130 | delete_block(blocks(i))
131 | add_line(parent,[line_in_pts(end,:); line_out_pts(1,:)])
132 | end
133 | end
134 | end
135 |
136 | function [] = tryAlignBlocksBackward(layout)
137 | layout.Value.dirty = 0;
138 | % Convert adjMatrix to boolean
139 | layout.Value.adjBool = cell2mat(arrayfun(@(i) ~cellfun(@isempty,layout.Value.adjMatrix(:,i)),1:size(layout.Value.adjMatrix,2),'uni',0));
140 | % Get blocks without outputs (adj i-column all 0)
141 | roots = layout.Value.blocks(arrayfun(@(i) all(layout.Value.adjBool(:,i)==0),1:size(layout.Value.adjBool,2)));
142 | % Also use minimum rank (right-most) blocks
143 | roots = union(roots,layout.Value.blocks(layout.Value.ranks==min(layout.Value.ranks)));
144 | % Layout blocks
145 | layout.Value.unvisited = layout.Value.blocks;
146 | for i = 1:length(roots)
147 | tryAlignBlocksBackward2(roots(i),layout);
148 | end
149 | end
150 |
151 | function [] = tryAlignBlocksForward(layout)
152 | layout.Value.dirty = 0;
153 | % Get blocks without inputs (adj i-row all 0)
154 | leaf = layout.Value.blocks(arrayfun(@(i) all(layout.Value.adjBool(i,:)==0),1:size(layout.Value.adjBool,1)));
155 | % Also use maximum rank (left-most) blocks
156 | leaf = union(leaf,layout.Value.blocks(layout.Value.ranks==max(layout.Value.ranks)));
157 | % Layout blocks
158 | layout.Value.unvisited = layout.Value.blocks;
159 | for i = 1:length(leaf)
160 | tryAlignBlocksForward2(leaf(i),layout);
161 | end
162 | end
163 |
164 | function [] = tryAlignBlocksForward2(block,layout)
165 | if isempty(find(layout.Value.unvisited==block,1)), return; end
166 | layout.Value.unvisited = setdiff(layout.Value.unvisited,block);
167 | blk_idx = str2double(get(block,'tag'));
168 | parents = layout.Value.blocks(layout.Value.adjBool(blk_idx,:));
169 | if ~isempty(parents)
170 | parent_idx = str2double(get(parents,'tag'));
171 | if iscell(parent_idx), parent_idx = cell2mat(parent_idx); end
172 | parents = parents(layout.Value.ranks(parent_idx)>layout.Value.ranks(blk_idx));
173 | end
174 | if ~isempty(parents)
175 | parent_idx = str2double(get(parents(1),'tag'));
176 | adj = layout.Value.adjMatrix{blk_idx,parent_idx};
177 | if adj(2) ~= -1
178 | oports = matsim.utils.getBlockPorts(parents(1),'output');
179 | ports = matsim.utils.getBlockPorts(block,'input');
180 | if strcmpi(get(ports(adj(3)),'porttype'),'inport')
181 | port_pos = get(oports(adj(2)),'position');
182 | else
183 | special = matsim.utils.getBlockPorts(block,'special');
184 | order = find(special==ports(adj(3)));
185 | port_pos = get(oports(adj(2)),'position');
186 | port_pos(2) = port_pos(2)+42*order;
187 | end
188 | iport_pos = get(ports(adj(3)),'position');
189 | else
190 | block_pos = get(block,'Position');
191 | parent_pos = get(parents(1),'Position');
192 | port_pos = [parent_pos(1)+parent_pos(3), parent_pos(2)+ceil((parent_pos(4)-parent_pos(2))/2)];
193 | iport_pos = [block_pos(1), block_pos(2)+ceil((block_pos(4)-block_pos(2))/2)];
194 | end
195 | blockSizeRef = get(block,'Position');
196 | % width = blockSizeRef(3) - blockSizeRef(1);
197 | height = blockSizeRef(4) - blockSizeRef(2);
198 | yTL = port_pos(2)-(iport_pos(2)-blockSizeRef(2));
199 | location = [blockSizeRef(1) yTL blockSizeRef(3) yTL+height];
200 | set(block,'position',location)
201 | intermediateRank = setdiff(layout.Value.blocks(layout.Value.ranks>=layout.Value.ranks(blk_idx) & layout.Value.rankslayout.Value.ranks(child_idx)),block);
261 | if ~matsim.builder.common.detectOverlaps(block,intermediateRank,'OverlapType','Vertical') && ...
262 | ~matsim.builder.common.detectOverlaps(block,setdiff(layout.Value.blocks,block),'OverlapType','All')
263 | if port_pos(2) ~= layout.Value.centers(blk_idx,2)
264 | layout.Value.centers(blk_idx,2) = port_pos(2);
265 | layout.Value.dirty = 1; % Something changed
266 | end
267 | else
268 | location = [blockSizeRef(1) layout.Value.centers(blk_idx,2)-ceil(height/2) blockSizeRef(3) layout.Value.centers(blk_idx,2)+floor(height/2)];
269 | set(block,'position',location)
270 | end
271 | end
272 | parents = layout.Value.blocks(layout.Value.adjBool(blk_idx,:));
273 | for i = 1:length(parents)
274 | tryAlignBlocksBackward2(parents(i),layout);
275 | end
276 | end
277 |
278 | function [] = getRank(layout)
279 | % Find block rank based on x-position
280 | layout.Value.ranks = zeros(1,length(layout.Value.blocks));
281 | centers = zeros(length(layout.Value.blocks),2);
282 | for i = 1:length(layout.Value.blocks)
283 | blockSizeRef = get(layout.Value.blocks(i),'Position');
284 | width = blockSizeRef(3) - blockSizeRef(1);
285 | height = blockSizeRef(4) - blockSizeRef(2);
286 | centers(i,:) = [blockSizeRef(1)+ceil(width/2), blockSizeRef(2)+ceil(height/2)];
287 | end
288 | xcenters = [];
289 | for i = 1:size(centers,1)
290 | % HACK: Centers are not perfectly aligned
291 | if isempty(find(abs(centers(i,1)-xcenters)<5,1))
292 | xcenters = [xcenters, centers(i,1)];
293 | end
294 | end
295 | xcenters = sort(xcenters,2,'descend');
296 | for i = 1:length(layout.Value.blocks)
297 | blockSizeRef = get(layout.Value.blocks(i),'Position');
298 | width = blockSizeRef(3) - blockSizeRef(1);
299 | xcenter = blockSizeRef(1)+ceil(width/2);
300 | [~,layout.Value.ranks(i)] = min(abs(xcenters-xcenter));
301 | end
302 | end
303 |
304 | function [] = tryMoveBlocks(layout,sys)
305 | other_blocks = matsim.helpers.findBlock(sys,'SearchDepth',1);
306 | other_blocks = setdiff(other_blocks,[sys;layout.Value.blocks(:)]); % Remove self
307 | if isempty(other_blocks), return; end
308 | oBlocksSize = get(other_blocks,'Position');
309 | if iscell(oBlocksSize), oBlocksSize = cell2mat(oBlocksSize); end
310 | lBlocksSize = get(layout.Value.blocks,'Position');
311 | if iscell(lBlocksSize), lBlocksSize = cell2mat(lBlocksSize); end
312 |
313 | lBlocksSize(:,[2,4]) = lBlocksSize(:,[2,4])-min(lBlocksSize(:,2));
314 | lBlocksSize(:,[2,4]) = lBlocksSize(:,[2,4])+max(oBlocksSize(:,4))+42;
315 | arrayfun(@(b) set(layout.Value.blocks(b),'position',lBlocksSize(b,:)),1:numel(layout.Value.blocks))
316 | end
317 |
318 |
--------------------------------------------------------------------------------
/+matsim/+library/Subsystem.m:
--------------------------------------------------------------------------------
1 | classdef Subsystem < matsim.library.block
2 | %SUBSYSTEM Creates a simulink Subsystem block.
3 | % Syntax:
4 | % blk = Subsystem();
5 | % Creates an empty Subsystem block with no inports or outports
6 | % blk = Subsystem(INPUTS);
7 | % INPUTS blocks will be connected to the block input ports.
8 | % INPUTS can be:
9 | % - an empty cell {}
10 | % - a matsim block
11 | % - a number
12 | % - a cell array of the above
13 | % If INPUTS is a number a Constant block with that value will
14 | % be created.
15 | % blk = Subsystem(INPUTS,ARGS);
16 | % ARGS is an optional list of parameter/value pairs specifying simulink
17 | % block properties.
18 | %
19 | % Subsystem Methods:
20 | % enable - adds an enable port
21 | % trigger - adds a trigger port
22 | % reset - adds a reset port
23 | % action - adds an action port
24 | % in - adds an input port
25 | % out - adds an output port
26 | %
27 | % Example:
28 | % in1 = Constant('var1');
29 | % in2 = FromWorkspace('var2');
30 | % s = Subsystem({},'name','TEST'); % Subsystem with no inports
31 | % s.in(1,in1,'name','VAR1'); % Add inport connected to in1
32 | % s.in(2,in2,'name','VAR2'); % Add inport connected to in2
33 | % res = s.in(1)+s.in(2)./0.5; % This operation happens inside the Subsystem
34 | % s.out(1,res,'name','RES') % Add outport connected to res
35 | % s.enable(1) % Add enable port connected to a Constant
36 | %
37 | % See also BLOCK.
38 |
39 | properties (Access = protected)
40 | % Handle to input ports
41 | simInport
42 | % Handle to enable ports
43 | simEnable
44 | % Handle to trigger ports
45 | simTrigger
46 | % Handle to reset ports
47 | simReset
48 | % Handle to action ports
49 | simAction
50 | % Handle to output ports
51 | simOutport
52 | end
53 |
54 | methods
55 | function this = Subsystem(varargin)
56 | p = inputParser;
57 | p.CaseSensitive = false;
58 | p.KeepUnmatched = true;
59 | addOptional(p,'inputs',[],@(x) isnumeric(x) || iscell(x) || isa(x,'matsim.library.block'));
60 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
61 | parse(p,varargin{:})
62 |
63 | inputs = p.Results.inputs;
64 | if ~iscell(inputs)
65 | inputs = {inputs};
66 | end
67 |
68 | parent = matsim.helpers.getValidParent(inputs{:},p.Results.parent);
69 | args = matsim.helpers.unpack(p.Unmatched);
70 |
71 | if isempty(parent)
72 | parent = gcs;
73 | end
74 |
75 | this = this@matsim.library.block('BlockName','SubSystem','parent',parent,args{:});
76 |
77 | if this.getUserData('created') == 0
78 | % Subsystem was created, delete default content
79 | Simulink.SubSystem.deleteContents(this.handle);
80 |
81 | if matsim.helpers.isArgSpecified(p,'inputs')
82 | for i = 1:length(inputs)
83 | this.simInport = concat(this.simInport,matsim.library.block('BlockType','Inport','parent',this));
84 | end
85 | end
86 | else
87 | % Subsystem already exists, fill input and output ports
88 | inports = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','Inport');
89 | enables = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','EnablePort');
90 | triggers = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','TriggerPort');
91 | resets = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','ResetPort');
92 | actions = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','ActionPort');
93 | for i = 1:length(inports)
94 | this.simInport = concat(this.simInport,matsim.library.block('name',get(inports(i),'name'),'parent',this));
95 | end
96 | for i = 1:length(enables)
97 | this.simEnable = concat(this.simEnable,matsim.library.block('name',get(enables(i),'name'),'parent',this));
98 | end
99 | for i = 1:length(triggers)
100 | this.simTrigger = concat(this.simTrigger,matsim.library.block('name',get(triggers(i),'name'),'parent',this));
101 | end
102 | for i = 1:length(resets)
103 | this.simReset = concat(this.simReset,matsim.library.block('name',get(resets(i),'name'),'parent',this));
104 | end
105 | for i = 1:length(actions)
106 | this.simAction = concat(this.simAction,matsim.library.block('name',get(actions(i),'name'),'parent',this));
107 | end
108 | outports = matsim.helpers.findBlock(this.handle,'SearchDepth',1,'BlockType','Outport');
109 | for i = 1:length(outports)
110 | this.simOutport = concat(this.simOutport,matsim.library.block('name',get(outports(i),'name'),'parent',this));
111 | end
112 | end
113 |
114 | if matsim.helpers.isArgSpecified(p,'inputs')
115 | this.setInputs(inputs);
116 | end
117 | end
118 |
119 | function enable = enable(this,varargin)
120 | %ENABLE Adds an enable port
121 | % Syntax:
122 | % s.enable(INPUT)
123 | % INPUT block will be connected to the block enable port.
124 | % INPUT can be:
125 | % - an empty cell {}
126 | % - a matsim block
127 | % - a number
128 | % If INPUT is a number a Constant block with that value will
129 | % be created.
130 | %
131 | % Example:
132 | % s = Subsystem({{}});
133 | % s.enable(Delay(1))
134 |
135 | p = inputParser;
136 | p.CaseSensitive = false;
137 | p.KeepUnmatched = true;
138 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
139 | parse(p,varargin{:})
140 |
141 | input = p.Results.input;
142 | args = matsim.helpers.unpack(p.Unmatched);
143 |
144 | if isempty(this.simEnable)
145 | enable = matsim.library.block('BlockType','EnablePort','parent',this,args{:});
146 | this.setInput(length(this.simEnable)+1,'value',input,'type','enable');
147 | this.simEnable = concat(this.simEnable,enable);
148 | else
149 | this.setInput(length(this.simEnable),'value',input,'type','enable');
150 | end
151 | end
152 |
153 | function trigger = trigger(this,varargin)
154 | %TRIGGER Adds a trigger port
155 | % Syntax:
156 | % s.trigger(INPUT)
157 | % INPUT block will be connected to the block trigger port.
158 | % INPUT can be:
159 | % - an empty cell {}
160 | % - a matsim block
161 | % - a number
162 | % If INPUT is a number a Constant block with that value will
163 | % be created.
164 | %
165 | % Example:
166 | % s = Subsystem({{}});
167 | % s.trigger(Delay(1))
168 |
169 | p = inputParser;
170 | p.CaseSensitive = false;
171 | p.KeepUnmatched = true;
172 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
173 | parse(p,varargin{:})
174 |
175 | input = p.Results.input;
176 | args = matsim.helpers.unpack(p.Unmatched);
177 |
178 | if isempty(this.simTrigger)
179 | trigger = matsim.library.block('BlockType','TriggerPort','parent',this,args{:});
180 | this.setInput(length(this.simTrigger)+1,'value',input,'type','trigger');
181 | this.simTrigger = concat(this.simTrigger,trigger);
182 | else
183 | this.setInput(length(this.simTrigger),'value',input,'type','trigger');
184 | end
185 | end
186 |
187 | function reset = reset(this,varargin)
188 | %RESET Adds a reset port
189 | % Syntax:
190 | % s.reset(INPUT)
191 | % INPUT block will be connected to the block reset port.
192 | % INPUT can be:
193 | % - an empty cell {}
194 | % - a matsim block
195 | % - a number
196 | % If INPUT is a number a Constant block with that value will
197 | % be created.
198 | %
199 | % Example:
200 | % s = Subsystem({{}});
201 | % s.reset(Delay(1))
202 |
203 | p = inputParser;
204 | p.CaseSensitive = false;
205 | p.KeepUnmatched = true;
206 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
207 | parse(p,varargin{:})
208 |
209 | input = p.Results.input;
210 | args = matsim.helpers.unpack(p.Unmatched);
211 |
212 | if isempty(this.simReset)
213 | reset = matsim.library.block('BlockType','ResetPort','parent',this,args{:});
214 | this.setInput(length(this.simReset)+1,'value',input,'type','reset');
215 | this.simReset = concat(this.simReset,reset);
216 | else
217 | this.setInput(length(this.simReset),'value',input,'type','reset');
218 | end
219 | end
220 |
221 | function action = action(this,varargin)
222 | %ACTION Adds an action port
223 | % Syntax:
224 | % s.action(INPUT)
225 | % INPUT block will be connected to the block action port.
226 | % INPUT can be:
227 | % - an empty cell {}
228 | % - a matsim block
229 | % - a number
230 | % If INPUT is a number a Constant block with that value will
231 | % be created.
232 | %
233 | % Example:
234 | % s = Subsystem({{}});
235 | % s.action(...)
236 |
237 | p = inputParser;
238 | p.CaseSensitive = false;
239 | p.KeepUnmatched = true;
240 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
241 | parse(p,varargin{:})
242 |
243 | input = p.Results.input;
244 | args = matsim.helpers.unpack(p.Unmatched);
245 |
246 | if isempty(this.simAction)
247 | action = matsim.library.block('BlockType','ActionPort','parent',this,args{:});
248 | this.setInput(length(this.simAction)+1,'value',input,'type','ifaction');
249 | this.simAction = concat(this.simAction,action);
250 | else
251 | this.setInput(length(this.simAction),'value',input,'type','ifaction');
252 | end
253 | end
254 |
255 | function in = in(this,index,varargin)
256 | %IN Adds an input port
257 | % Syntax:
258 | % s.in(INDEX,INPUT)
259 | % INPUT block will be connected to the INDEX block input port.
260 | % INPUT can be:
261 | % - an empty cell {}
262 | % - a matsim block
263 | % - a number
264 | % If INPUT is a number a Constant block with that value will
265 | % be created.
266 | % s.in(INDEX,ARGS)
267 | % ARGS is an optional list of parameter/value pairs specifying simulink
268 | % block properties.
269 | %
270 | % Example:
271 | % s = Subsystem({{}}); % Subsystem with one (unconnected) inport
272 | % s.in(2,Gain(Constant(-1))) % Connect a Gain to the second inport
273 |
274 | p = inputParser;
275 | p.CaseSensitive = false;
276 | p.KeepUnmatched = true;
277 | addRequired(p,'index',@isnumeric)
278 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
279 | parse(p,index,varargin{:})
280 |
281 | index = p.Results.index;
282 | input = p.Results.input;
283 | args = matsim.helpers.unpack(p.Unmatched);
284 |
285 | if index <= length(this.simInport)
286 | % Return inport block
287 | in = this.simInport(index);
288 | set(in.handle,args{:});
289 | if ~any(strcmp(p.UsingDefaults,'input'))
290 | this.setInput(index,'value',input);
291 | end
292 | else
293 | % Create new inport
294 | in = matsim.library.block('BlockType','Inport','parent',this,args{:});
295 | this.simInport = concat(this.simInport,in);
296 | this.setInput(index,'value',input);
297 | end
298 | end
299 |
300 | function out = out(this,index,varargin)
301 | %OUT Adds an output port
302 | % Syntax:
303 | % s.out(INDEX,INPUT)
304 | % INPUT block will be connected to the INDEX block output port.
305 | % INPUT can be:
306 | % - an empty cell {}
307 | % - a matsim block
308 | % - a number
309 | % If INPUT is a number a Constant block with that value will
310 | % be created.
311 | % s.out(INDEX,ARGS)
312 | % ARGS is an optional list of parameter/value pairs specifying simulink
313 | % block properties.
314 | %
315 | % Example:
316 | % s = Subsystem({}); % Subsystem with no inport
317 | % s.in(1,'name','in1') % Creates an inport
318 | % s.in(2,'name','in2') % Creates an inport
319 | % s.out(1,'name','res'); % Creates an outport
320 | % s.out(1,s.in(1)+Constant(1,'parent',s)) % Connects outport inside subsystem
321 |
322 | p = inputParser;
323 | p.CaseSensitive = false;
324 | p.KeepUnmatched = true;
325 | addRequired(p,'index',@isnumeric)
326 | addOptional(p,'input',{},@(x) isnumeric(x) || isempty(x) || isa(x,'matsim.library.block'));
327 | parse(p,index,varargin{:})
328 |
329 | index = p.Results.index;
330 | input = p.Results.input;
331 | args = matsim.helpers.unpack(p.Unmatched);
332 |
333 | if index <= length(this.simOutport)
334 | % Return outport block
335 | out = this.simOutport(index);
336 | set(out.handle,args{:});
337 | if ~any(strcmp(p.UsingDefaults,'input'))
338 | out.setInputs({input});
339 | end
340 | else
341 | % Create new outport
342 | out = matsim.library.block('BlockType','Outport','parent',this,args{:});
343 | out.setInputs({input});
344 | this.simOutport = concat(this.simOutport,out);
345 | end
346 | end
347 |
348 | function ports = getPorts(this)
349 | ports = struct;
350 | ports.enable = this.simEnable;
351 | ports.trigger = this.simTrigger;
352 | ports.reset = this.simReset;
353 | ports.action = this.simAction;
354 | ports.inport = this.simInport;
355 | ports.outport = this.simOutport;
356 | end
357 | end
358 | end
359 |
--------------------------------------------------------------------------------
/+matsim/+library/block.m:
--------------------------------------------------------------------------------
1 | classdef block < handle
2 | %BLOCK Creates any simulink block.
3 | % Syntax:
4 | % blk = block('model',MODEL,'BlockName',NAME);
5 | % MODEL is the name of the library containing the desired block.
6 | % NAME is the name (prop: "Name") of the block to be created.
7 | % blk = block('model',MODEL,'BlockType',TYPE);
8 | % MODEL is the name of the library containing the desired block.
9 | % TYPE is the type (prop: "BlockType") of the block to be created.
10 | % blk = block('model',MODEL,'BlockType',TYPE,ARGS);
11 | % ARGS is an optional list of parameter/value pairs specifying simulink
12 | % block properties.
13 | % blk = [block1+block2./0.5, block3];
14 | % Operations on matsim blocks will be converted into simulink
15 | % connections and blocks. "a+b" will create an Add block, "[a,b]" will
16 | % create a Mux block, "a(-1)" will create a Delay block.
17 | %
18 | % Supported operations:
19 | % Math operations
20 | % plus (a+b), minus (a-b), unary minus (-a)
21 | % product (a.*b), matrix product (a*b), division (a./b), matrix division (a/b)
22 | % power (a.^b), matrix power (a^b)
23 | % Logical operations
24 | % greater than (a > b), less than (a < b), greater or equal than (a >= b), less or equal than (a <= b)
25 | % equal to (a == b), not equal to (a ~= b)
26 | % and (a && b), or (a || b), not (~a)
27 | % Array operations
28 | % horzcat ([a,b,c]), vertcat ([a;b;c])
29 | %
30 | % block Methods:
31 | % setInputs - set all block inputs
32 | % setInput - set a specific block inport input
33 | % outport - "selects" a block outport or sets its name
34 | %
35 | % Example:
36 | % blk = block('model','simulink','BlockType','Gain','ShowName','off','ForegroundColor','red');
37 |
38 | properties (Access = private)
39 | % Handle to simulink block
40 | simBlock
41 | % Handles to input blocks
42 | simInputs
43 | % Handle to output port
44 | simSelectedOutport
45 | % Fixed height
46 | simHeight
47 | % Spacer block
48 | simIsVirtual
49 | end
50 |
51 | methods (Access = public)
52 | function this = block(varargin)
53 | p = inputParser;
54 | p.CaseSensitive = false;
55 | p.KeepUnmatched = true;
56 | addParamValue(p,'BlockName','',@ischar);
57 | addParamValue(p,'BlockType','',@ischar);
58 | addParamValue(p,'model','simulink',@ischar);
59 | addParamValue(p,'copy',false,@islogical);
60 | addParamValue(p,'parent','',@(x) ischar(x) || ishandle(x) || isa(x,'matsim.library.block') || isa(x,'matsim.library.simulation'));
61 | parse(p,varargin{:})
62 |
63 | block_name = p.Results.BlockName;
64 | block_type = p.Results.BlockType;
65 | model = p.Results.model;
66 | copy = p.Results.copy;
67 | strParent = matsim.helpers.getBlockPath(p.Results.parent);
68 | args = matsim.helpers.validateArgs(p.Unmatched);
69 |
70 | if isempty(strParent)
71 | strParent = gcs;
72 | end
73 |
74 | if any(strcmp(args,'name'))
75 | % Find existing block
76 | name = p.Unmatched.name;
77 | match = matsim.helpers.findBlock(strParent,'BlockName',name,'SearchDepth',1);
78 | if ~isempty(match)
79 | this.simBlock = get_param(match{1},'handle');
80 | blk = this.getUserData('block');
81 | if ~isempty(blk)
82 | % Block was a MATSIM block, reuse
83 | this.simBlock = blk.handle;
84 | this.simInputs = blk.inputs;
85 | this.simSelectedOutport = blk.simSelectedOutport;
86 | if ~copy
87 | this.setUserData('block',{});
88 | this.setUserData('block',this);
89 | % this = blk;
90 | end
91 | this.setUserData('created',2)
92 | else
93 | % Block was a SIMULINK block
94 | this.setUserData('block',this);
95 | this.setUserData('created',1)
96 | this.simInputs = struct('inport',{{}},'enable',{{}},'trigger',{{}},'reset',{{}},'ifaction',{{}});
97 | this.setInputsFromBlock();
98 | end
99 | this.simSelectedOutport = 1;
100 | return;
101 | end
102 | end
103 |
104 | % Create block
105 | match = matsim.helpers.findBlock(model,'BlockName',block_name,'BlockType',block_type);
106 | if isempty(match)
107 | match = matsim.helpers.findBlock(model,'BlockName',block_name,'BlockType',block_type,'LookUnderMasks','all');
108 | end
109 | if ~isempty(match)
110 | this.simBlock = add_block(match{1},strjoin({strParent,get_param(match{1},'name')},'/'),'MakeNameUnique','on',args{:});
111 | this.setUserData('block',this)
112 | this.setUserData('created',0)
113 | this.simSelectedOutport = 1;
114 | this.simInputs = struct('inport',{{}},'enable',{{}},'trigger',{{}},'reset',{{}},'ifaction',{{}});
115 | % Set position to far right
116 | blockSizeRef = this.get('position');
117 | this.set('position',[1e4, 0, 1e4+blockSizeRef(3)-blockSizeRef(1), blockSizeRef(4)-blockSizeRef(2)])
118 | else
119 | error('Invalid block name')
120 | end
121 | end
122 |
123 | function in = inputs(this)
124 | in = this.simInputs;
125 | end
126 | function out = outport(this,varargin)
127 | %OUTPORT "Selects" a block outport or sets its name.
128 | % Syntax:
129 | % out = blk.outport(INDEX)
130 | % Selects INDEX output port of the block. You can use this
131 | % to specify which outport of the block to use as input for
132 | % another block.
133 | % blk.outport(INDEX,'name',SIGNAME)
134 | % Set SIGNAME as name of the INDEX outport. Also sets the
135 | % label of the outgoing line.
136 | %
137 | % Example:
138 | % blk = Demux('Outputs',[1 1]);
139 | % blk.outport(1,'name','OUT1');
140 | % Terminator(blk.outport(1));
141 | % sc = Scope(blk.outport(2));
142 |
143 | p = inputParser;
144 | p.CaseSensitive = false;
145 | p.KeepUnmatched = true;
146 | addOptional(p,'index',[],@isnumeric);
147 | addParamValue(p,'name',[],@ischar);
148 | parse(p,varargin{:})
149 |
150 | index = p.Results.index;
151 | name = p.Results.name;
152 | if ~isempty(index)
153 | out = matsim.library.block('copy',true,'parent',matsim.helpers.getValidParent(this),'name',this.get('name'));
154 | out.simSelectedOutport = index;
155 | else
156 | index = this.simSelectedOutport;
157 | out = index;
158 | end
159 | if ~any(strcmp(p.UsingDefaults,'name'))
160 | ph = get(this,'porthandles');
161 | set(ph.Outport(index),'SignalNameFromLabel',name);
162 | end
163 | end
164 |
165 | function h = handle(this)
166 | h = this.simBlock;
167 | end
168 | function p = get_param(this,prop)
169 | p = this.get(prop);
170 | end
171 | function p = get(this,prop)
172 | p = get(this.simBlock,prop);
173 | end
174 | function [] = set_param(this,prop,value)
175 | this.set(prop,value);
176 | end
177 | function [] = set(this,prop,value)
178 | if iscell(prop)
179 | arrayfun(@(i) this.set(prop{i},prop{i+1}), 1:2:length(prop)-1)
180 | return
181 | end
182 |
183 | if strcmpi(prop,'name')
184 | parent = matsim.helpers.getValidParent(this);
185 | match = matsim.helpers.findBlock(parent,'BlockName',value,'SearchDepth',1,'Exact',false);
186 | if isempty(match)
187 | this.safe_set(prop,matsim.helpers.validateArgs(value));
188 | else
189 | idx = 1+length(match);
190 | this.safe_set(prop,sprintf('%s%d',matsim.helpers.validateArgs(value),idx));
191 | end
192 | elseif strcmpi(prop,'height')
193 | this.simHeight = value;
194 | elseif strcmpi(prop,'isvirtual')
195 | this.simIsVirtual = value;
196 | else
197 | this.safe_set(prop,matsim.helpers.validateArgs(value));
198 | end
199 | end
200 | end
201 |
202 | methods (Access = private)
203 | function [] = safe_set(this,prop,value)
204 | try
205 | set(this.simBlock,prop,value);
206 | catch ex
207 | warning(ex.message)
208 | end
209 | end
210 | function [] = setInputsFromBlock(this)
211 | ports = matsim.utils.getBlockPorts(this,'input');
212 | for i=1:length(ports)
213 | line = get(ports(i),'line');
214 | if (line == -1 || get(line,'SrcBlockHandle') == -1)
215 | this.setInput(i,'value',{},'type',get(ports(i),'porttype'));
216 | else
217 | src_block = matsim.library.block('name',get(get(line,'SrcBlockHandle'),'name'),'parent',matsim.helpers.getValidParent(this));
218 | src_port = get(get(line,'SrcPortHandle'),'PortNumber');
219 | this.setInput(i,'value',src_block,'srcport',src_port,'type',get(ports(i),'porttype'));
220 | end
221 | end
222 | end
223 | end
224 |
225 | methods (Access = protected)
226 | function [] = setInputs(this,varargin)
227 | %SETINPUTS Set all block inputs
228 | % Syntax:
229 | % blk.setInputs(INPUTS)
230 | % INPUTS blocks will be connected to the block input ports.
231 | % INPUTS can be:
232 | % - an empty cell {}
233 | % - a matsim block
234 | % - a number
235 | % - a cell array of the above
236 | % If INPUTS is a number a Constant block with that value will
237 | % be created.
238 | %
239 | % Example:
240 | % in1 = FromWorkspace('var1');
241 | % in2 = Constant('var2');
242 | % blk = block('model','simulink','BlockType','Mux','Inputs','4');
243 | % blk.setInputs({in1,{},0,in2})
244 |
245 | p = inputParser;
246 | p.CaseSensitive = false;
247 | p.KeepUnmatched = true;
248 | addOptional(p,'value',[]);
249 | parse(p,varargin{:})
250 |
251 | value = p.Results.value;
252 | parent = matsim.helpers.getValidParent(this);
253 | if matsim.helpers.isArgSpecified(p,'value')
254 | validatedInputs = matsim.helpers.validateInputs(value,parent);
255 | if ~iscell(validatedInputs)
256 | validatedInputs = {validatedInputs};
257 | end
258 | this.simInputs.inport = validatedInputs(cellfun(@(x) strcmp(x.type,'inport'),validatedInputs));
259 | this.simInputs.enable = validatedInputs(cellfun(@(x) strcmp(x.type,'enable'),validatedInputs));
260 | this.simInputs.trigger = validatedInputs(cellfun(@(x) strcmp(x.type,'trigger'),validatedInputs));
261 | this.simInputs.reset = validatedInputs(cellfun(@(x) strcmp(x.type,'reset'),validatedInputs));
262 | this.simInputs.ifaction = validatedInputs(cellfun(@(x) strcmp(x.type,'ifaction'),validatedInputs));
263 | end
264 | end
265 | function [] = setInput(this,varargin)
266 | %SETINPUT Set a specific block inport input
267 | % Syntax:
268 | % blk.setInput(INDEX,'value',VALUE,'srcport',SRCPORT,'type',TYPE)
269 | % SRCPORT of block VALUE will be connected to INDEX input
270 | % port of the block.
271 | % VALUE can be:
272 | % - an empty cell {}
273 | % - a matsim block
274 | % - a number
275 | % If VALUE is a number a Constant block with that value will
276 | % be created.
277 | % TYPE is optional and can be "inport", "enable", "trigger", "reset", "ifaction".
278 | %
279 | % Example:
280 | % in1 = FromWorkspace('var1');
281 | % blk = block('model','simulink','BlockType','Mux','Inputs','2');
282 | % blk.setInput(1,'value',in1,'srcport',1)
283 |
284 | p = inputParser;
285 | p.CaseSensitive = false;
286 | p.KeepUnmatched = true;
287 | addRequired(p,'index',@isnumeric);
288 | addParamValue(p,'value',{});
289 | addParamValue(p,'srcport',1,@isnumeric);
290 | addParamValue(p,'type','inport',@ischar);
291 | parse(p,varargin{:})
292 |
293 | index = p.Results.index;
294 | srcport = p.Results.srcport;
295 | type = lower(p.Results.type);
296 |
297 | parent = matsim.helpers.getValidParent(this);
298 | new_input = matsim.helpers.validateInputs(p.Results.value,parent);
299 | new_input.type = type;
300 | if matsim.helpers.isArgSpecified(p,'srcport')
301 | new_input.srcport = srcport;
302 | end
303 |
304 | this.simInputs.(type){index} = new_input;
305 | end
306 |
307 | function setMaskParam(this,name,value)
308 | mname = get(this.simBlock,'MaskNames');
309 | mvalue = get(this.simBlock,'MaskValues');
310 | if isnumeric(value)
311 | mvalue{find(strcmp(mname,name),1)} = mat2str(value);
312 | elseif ischar(value)
313 | mvalue{find(strcmp(mname,name),1)} = value;
314 | end
315 | set(this.simBlock,'MaskValues',mvalue)
316 | end
317 |
318 | function p = getUserData(this,prop)
319 | data = get(this.simBlock,'UserData');
320 | if ~isempty(data) && isfield(data,prop)
321 | p = data.(prop);
322 | else
323 | p = [];
324 | end
325 | end
326 | function [] = setUserData(this,prop,value)
327 | data = get(this.simBlock,'UserData');
328 | data.(prop) = value;
329 | set(this.simBlock,'UserData',data);
330 | end
331 | end
332 |
333 | methods (Access = public)
334 | % From https://it.mathworks.com/help/matlab/matlab_oop/implementing-operators-for-your-class.html
335 | %% Add
336 | function r = plus(b1,b2)
337 | r = matsim.library.binary_operator(b1,b2,'BlockName','Add');
338 | end
339 | function r = minus(b1,b2)
340 | r = matsim.library.binary_operator(b1,b2,'BlockName','Subtract');
341 | end
342 | function r = uplus(b1)
343 | r = b1;
344 | end
345 | function r = uminus(b1)
346 | % r = matsim.library.binary_operator(b1,-1,'BlockName','Product');
347 | r = Gain(b1,'Gain',-1);
348 | end
349 |
350 | %% Product
351 | function r = times(b1,b2)
352 | r = matsim.library.binary_operator(b1,b2,'BlockName','Product');
353 | end
354 | function r = mtimes(b1,b2)
355 | r = matsim.library.binary_operator(b1,b2,'BlockName','Product','Multiplication','Matrix(*)');
356 | end
357 |
358 | %% Division
359 | function r = rdivide(b1,b2)
360 | r = matsim.library.binary_operator(b1,b2,'BlockName','Divide');
361 | end
362 | function r = ldivide(b1,b2)
363 | r = matsim.library.binary_operator(b2,b1,'BlockName','Divide','Multiplication','Matrix(*)');
364 | end
365 | function r = mrdivide(b1,b2)
366 | r = matsim.library.binary_operator(b1,b2,'BlockName','Divide','Multiplication','Matrix(*)');
367 | end
368 | function r = mldivide(b1,b2)
369 | r = matsim.library.binary_operator(b2,b1,'BlockName','Divide','Multiplication','Matrix(*)');
370 | end
371 |
372 | %% Math operation
373 | function r = power(b1,b2)
374 | if isnumeric(b2) && isscalar(b2) && b2==1
375 | r = b1;
376 | elseif isnumeric(b2) && isscalar(b2) && b2==2
377 | r = matsim.library.binary_operator(b1,b1,'BlockName','Product');
378 | else
379 | r = matsim.library.binary_operator(b1,b2,'BlockName','Math Function','Function','pow');
380 | end
381 | end
382 | function r = mpower(b1,b2)
383 | r = matsim.library.binary_operator(b1,b2,'BlockName','Math Function','Function','pow');
384 | end
385 |
386 | %% Compare
387 | function r = lt(b1,b2)
388 | r = matsim.library.binary_operator(b1,b2,'BlockName','Relational Operator','Operator','<');
389 | end
390 | function r = gt(b1,b2)
391 | r = matsim.library.binary_operator(b1,b2,'BlockName','Relational Operator','Operator','>');
392 | end
393 | function r = le(b1,b2)
394 | r = matsim.library.binary_operator(b1,b2,'BlockName','Relational Operator','Operator','<=');
395 | end
396 | function r = ge(b1,b2)
397 | r = matsim.library.binary_operator(b1,b2,'BlockName','Relational Operator','Operator','>=');
398 | end
399 | function r = ne(b1,b2)
400 | r = matsim.library.binary_operator(b1,b2,'Relational Operator','Operator','~=');
401 | end
402 | function r = eq(b1,b2)
403 | r = matsim.library.binary_operator(b1,b2,'BlockName','Relational Operator','Operator','==');
404 | end
405 |
406 | %% Logical
407 | function r = and(b1,b2)
408 | r = matsim.library.binary_operator(b1,b2,'BlockName','Logical Operator','Operator','AND');
409 | end
410 | function r = or(b1,b2)
411 | r = matsim.library.binary_operator(b1,b2,'BlockName','Logical Operator','Operator','OR');
412 | end
413 | function r = not(b1)
414 | r = matsim.library.unary_operator(b1,'BlockName','Logical Operator','Operator','NOT');
415 | end
416 |
417 | %% Vector
418 | function r = concat(varargin)
419 | r = [];
420 | for v = 1:length(varargin)
421 | if isempty(r)
422 | r = varargin{v};
423 | else
424 | r(end+1:end+length(varargin{v})) = varargin{v};
425 | end
426 | end
427 | end
428 | function r = horzcat(varargin)
429 | r = matsim.library.Mux(varargin);
430 | end
431 | function r = vertcat(varargin)
432 | r = matsim.library.Mux(varargin);
433 | end
434 |
435 | %% Subscript
436 | function varargout = subsref(A,S)
437 | if length(S) == 1
438 | switch S(1).type
439 | case '()'
440 | if length(S(1).subs) == 1 && length(S(1).subs{1}) == 1 && S(1).subs{1} < 0
441 | dl = abs(S(1).subs{1});
442 | varargout{1} = matsim.library.Delay(A,'DelayLength',dl);
443 | return
444 | elseif length(S(1).subs) == 2 && all(S(1).subs{1} > 0) && length(S(1).subs{2}) == 1
445 | indices = S(1).subs{1};
446 | insize = S(1).subs{2};
447 | varargout{1} = matsim.library.Selector(A,'Indices',indices,'InputPortWidth',insize);
448 | return
449 | end
450 | otherwise
451 | end
452 | end
453 | [varargout{1:nargout}] = builtin('subsref',A,S);
454 | end
455 | end
456 | end
457 |
--------------------------------------------------------------------------------