Accessing Simulink Functions from inside Atomic Subchart - matlab

For reasons far outside my control, I have now been thrust into doing MATLAB/Simulink/Stateflow work. I've done the On-Ramp training, and already I despise how unintuitive it is to do things that are just common and easy in any text-based programming language.
So here's my issue: I am trying to create Stateflow subcharts that I can reuse like a function, like a series of steps to take when requesting a series of responses from an SPI bus. I want to be able to use this subchart from within other subcharts in the same parent Stateflow diagram. My research has so far led me to Atomic Subcharts.
The problem is, my subchart relies on a number of Simulink Functions, which in turn call S-Functions to communicate with an STM32 target. I can make the subchart, no problem, with the Simulink Functions in the root Stateflow diagram. But when I convert the subchart into an Atomic Subchart, it can no longer detect the Simulink functions, giving me errors in the subchart for them.
All of this I am doing inside of a library as common code for a particular chip we use on a number of in-house circuit boards. As a final wrinkle, this whole thing is being used inside a much larger system and uses the "after()" transition between states so that the RTOS can go do other things. As far as I am aware, I cannot do the same thing inside of a Simulink or MATLAB function and HAVE to do this in Stateflow, which means I can't just make a normal "do all SPI reads" function but need a "Stateflow function".
Is there any way to access Simulink Functions from inside an Atomic Function?
Is there any other way to reuse a Stateflow diagram like a function, so that I can update the root diagram without having to modify the same copy/pasted diagram code in multiple places?
I also cannot use graphical functions because these diagrams have loops, and apparently you can't backtrack inside of a graphical function.

So I found an answer while working through this problem with more Stateflow experience than me. He mentioned the idea of "concurrently running states".
What you do is, at the root state, group your normal state into a subchart. Then for any reusable code you want to call like a function, make that a separate state machine with its own substates that defaults to an Idle state. You can then declare a local event in your main state to move the "function" state from its idle state, and have your main state wait for the "function" to return to its idle condition.
Your "function calls" in your main states are going to always have 2 states as you want for the "function" to be in its idle state, and then wait again until it returns to its idle state, but if you've got a subchart of sufficient complexity, this is a lot more compact than trying to copy and paste the same behavior in multiple subcharts and having to modify them later. Plus, you get the function-like behavior of only having to modify your "function" subchart to modify the behavior all across your stateflow diagram.
Information about how to create parallel running states can be found here:
https://www.mathworks.com/help/stateflow/gs/parallelism.html
https://www.mathworks.com/help/stateflow/gs/events.html

Related

How to pass multiple variables from one model to another model (inner/outer)

Let's say we have the following model:
Collector:
model Collector
Real collect_here;
annotation(defaultComponentPrefixes="inner");
end Collector;
and the following model potentially multiple times:
model Calculator
outer Collector collector;
Real calculatedVariable = 2*time;
equation
calculatedVariable = collector.collect_here;
end Calculator;
The code above works if calcModel is present only once in the system to be simulated. If the model exists more than once I get a singular system. This is demonstrated by the Example below. Changing the parameter works either gives a working or failing system.
model Example
parameter Boolean works = true;
inner Collector collector;
Calculator calculator1;
Calculator calculator2 if not works;
end Example;
Using an array inside the collector to pass multiple variables in it doesn't solve it.
Another possible way to solve this is possible by use of connectors, but I only made it work with one calcModel.
Using multiple instances of Calculator does brake the model, as the single variable calculatedVariable will have multiple equations trying to compute its value. Therefore Dymola complains that the system is structurally singular, in this case meaning that there are more equations than variables in the resulting system of equations.
To give a bit more of an insight: Actually checking Collector will fail, as since Modelica 3.0 every component has to be balanced (meaning it has to have as many unknowns as states), which is not the case for Collector as it does have one unknown but no equation. This strongly limits the possible applications for the inner/outer construct as basically every variable has to be computed where it is defined.
In the given example this is compensated in the overall system if exactly one Calculator is used. So this single combination will work. Although this works, it is something that should not be done - for the obvious reason of being very error-prone (and all sub-models should pass the check).
Your question on how to solve this issue actually misses a description of what the issue actually is. There are some cases in my mind that your approach could be useful for:
You want to plot multiple variables from a single point, which would be collector. For this purpose "variable selections" should be the most straight-forward way to go: see Dymola Manual Vol. 1, Section "4.3.11 Matching and variable selections" on how to apply them.
You want to carry out some mathematical operation on that variables. Then it could be useful to have a vectorized input of variable size. This enables an arbitrary number of connections to this input. For an example of this take a look at: Modelica.Blocks.Math.MultiSum
You want to route multiple signals between different models (which is unlikely judging from your description, but still): Then expandable connectors would be a good possibility. To get an impression of what that does take a look at Modelica.Blocks.Examples.BusUsage.
Hope this helps, otherwise please specify more clearly what you actually want to achieve with your code.
I prepared a demonstrative library for such scenario some days ago. You can access it at https://gist.github.com/beutlich/e630b2bf6cdf3efe96e5e9a637124fe1. If you read the documentation on Example2 you can see the link to an article from H. Elmqvis et. al., which is the clue to your problem. That is, you need a connector, and inherited connects from every Calculator to the one Collector.

"Treat as atomic " effect on generated code

What impact will it cause on the generated code, if a subsystem block is marked "Treat as atomic". Even with option as unchecked, there is no use of virtual keyword in the generated code. Please explain.
As a very general statement, consider:
During simulation, non-atomic (virtual) subsystems are just visual grouping elements.
During simulation, Simulink runs the content of atomic subsystems as if they were in a separate function of their own.
They will also appear as separate functions in the generated code (although this also depends on their function packaging attributes).

Static Variables in Simulink S-Function Builder

I am currently working on the implementation of some C-Code in a Simulink model using the S-Function Builder block.
The code uses various timers and counters, which are defined as static variables to enable the access to the data in following simulation steps.
However, if I start the simulation MATLAB crashes without error message ('Fatal Exception'). To test I defined the variables without the 'static' statement. The Simulation works in this case, however with (logically) wrong results of the S-Function.
Has anybody else faced similar issues or knows how to declare static variables in Simulink?
P.S.
I know I could use Work Vectors, which I do not intend to do, since it would result in huge efforts in adopting the function to do so.
Furthermore I could simply build a feed-back loop in the model using a memory block. For approximately 100 variables this solution would also be pretty impractical.
Not a solution, but a possible workaround is to use the coder.ceval functionality. I have used this to wrap a C-function with similar (static variables used as counters) function. The coder.ceval call is then placed in an embedded matlab block. Possibly some definitions of the interfaces must also be made (structures / bus objects).
Check coder.ceval, coder.rref and coder.wref for the call structure.
It seems like it was a bug in Simulink or the MinGW Compiler. However I tore down the code, ending up with it crashing with the call of one specific variable. I renamed the variable, since I could not find any error in the syntax. Now everything works fine...
The variable name had various underscores and capital letters - in case anyone makes similar experiences.

MATLAB organise external toolboxes or turn them into packages to prevent shadowing

I'm working on a large data analysis that incorporates lots of different elements and hence I make heavy use of external toolboxes and functions from file exchange and github. Adding them all to the path via startup.m is my current working method but I'm running into problems of shadowing function names across toolboxes. I don't want to manually change function names or turn them into packages, since a) it's a lot of work to check for shadowing and find all function calls and more importantly b) I'm often updating the toolboxes via git. Since I'm not the author all my changes would be lost.
Is there programmatic way of packaging the toolboxes to create their own namespaces? (With as little overhead as possible?)
Thanks for the help
You can achieve this. Basic idea is to make all functions in a private folder and have only one entry point visible. This entry point is the only file seeing the toolbox, and at the same time it sees the toolbox function first regardless of the order in the search path.
toolbox/exampleToolbox.m
function varargout=exampleToolbox(fcn,varargin)
fcn=str2func(fcn);
varargout=cell(1,nargout);
[varargout{:}]=fcn(varargin{:});
end
with toolbox/exampleToolbox/private/foo.m beeing an example function.
Now you can call foo(1,2,3) via exampleToolbox('foo',1,2,3)
The same technique could be used generating a class. This way you could use exampleToolbox.foo(1,2,3)

Add a Simulink block to set system-wide parameters

I have a Simulink model with a number of system-wide parameters that affect many different blocks. The way I deal with this right now is by encapsulating the entire model inside of a masked subsystem at the top level and managing the parameters there. Doing this makes the parameters visible to all blocks. However, I would rather have my model reside at the top level and include a parameters block there that I can use to manipulate the system parameters.
I don't know if pictures will help here, but they can't hurt:
The picture above shows an example of my current setup. Notice that the entire design is nested inside of a masked subsystem called "System Parameters"
This picture shows how I would like for the top level to appear. This seems to be a much more intuitive interface. It would also allow for much easier copying of my parameters block between models, which is my main interest in it. I would really like to convert it to a library block that I can use in a handful of models that are based on the same hardware system. However, the problem is that the parameters within the System Parameters block are not visible to the rest of the blocks in the model (at least not directly).
Is there a way that a block like the one in the second image could make its parameters easily available to the rest of the model?
For the parameters to be available to the other blocks, they need to be either in the model workspace or in the base workspace. You could add an initialise callback to your block that would copy the mask parameters to either workspace, but in my opinion, a much better practice would be to have a MATLAB script defining all the parameters in the base workspace that is called during the InitFcn model callback. You then just need to distribute that MATLAB script along with your model for the end user.