├── 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 | [![View Matsim on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://it.mathworks.com/matlabcentral/fileexchange/68436-matsim) ![logo](https://img.shields.io/badge/license-MIT-blue.svg) [![donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](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 | ![banner](/images/matsim-icon.png) 2 | 3 | # Matsim  ![logo](https://img.shields.io/badge/license-MIT-blue.svg) [![donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](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 | 21 | 22 | 23 | 24 | 25 | 29 | 33 | 34 | 35 | 36 | 41 | 48 | 49 | 50 | 51 | 54 | 61 | 62 |
                                       With MatsimWith simulink API
Create or load model 26 |
sys = simulation.load('my_model');
27 | sys.show()
28 |
30 |
sys = new_system('my_model');
31 | open_system(sys);
32 |
Add blocks 37 |
Vx = FromWorkspace('V_x');
38 | c = Constant(1);
39 | res = Vx+c;
40 |
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 |
Layout and connect 52 |
sys.layout();
53 |
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 |
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 | 62 | 63 | 66 |
55 |
% Create 2 Constant blocks
 56 | c1 = Constant(1,'name','c1');
 57 | c2 = Constant('var1','name','c2');
 58 | 
 59 | % Create a Max block with c1 and c2 as inputs
 60 | res = Max(c1,c2,'name','result');
61 |
 → 
 64 | 
 65 | 
67 | 68 | A block can become an input for the next. 69 | 70 | 71 | 72 | 75 | 76 | 79 | 80 |
73 |
Scope(Gain(FromWorkspace('var'),'Gain',0.5))
74 |
 → 
 77 | 
 78 | 
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 | 95 | 96 | 99 | 100 |
87 |
% Create 3 From Workspace blocks
 88 | u1 = FromWorkspace('u1');
 89 | u2 = FromWorkspace('u2');
 90 | u3 = FromWorkspace('u3');
 91 | 
 92 | % Complex mathematical expression
 93 | res = 1 - u1./(u2.*u3)
94 |
 → 
 97 | 
 98 | 
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 | --------------------------------------------------------------------------------