function simevents_animation(block)
% The goal of this code is to make the S-function behave like a Simulink
% block. During simulation of a model, the Simulink engine invokes this function at different times during the simulation
% Initialization step, update and output steps and the subfunctions
% corresponding to them get invoked in that same order. The initialization function gets invoked only once at the beginning of the
% simulation. Update and output functions get invoked at every time step (major & minor). Also, observe that the level-2
% S-Function has a lot of similarity with C S-Function that the Level-1
% S-Function.
setup(block)
function setup(block)
% We setup and register callback methods that are directly called during simulation.
% Number of input ports and dimensionality
% (1) indicates the output port number-change it to add multiple ports.
% Also do bookkeeping of these ports by documenting your code
block.NumInputPorts = 2;
block.InputPort(1).Dimensions = [2 1];
block.InputPort(1).DirectFeedthrough = false;
block.InputPort(2).Dimensions = [1 1];
block.InputPort(2).DirectFeedthrough = false;
% Number of output ports and dimensionality
block.NumOutputPorts = 0;
% (1) indicates the output port number-change it to add multiple ports.
% Also do bookkeeping of these ports by documenting your code
% block.OutputPort(1).Dimensions= 0;
% Number of continuous states, in our case we are assuming the state to be
% discrete, so set this variable to zero. Note that this choice will impact
% the choice of sample times. Try changing block.NumContStates = 1 and block.SampleTimes = [0 0] and you will
% see that the plot looks very discrete as we are depending on the solver
% to take the time steps. So, slowing the simulation down was a requirement
% that could be enforced by making this block discrete and giving it a
% sample time. The solver cannot bypass this "slow" block in the model. You
% end up getting a nice plot with deterioration in simulation performance.
block.NumContStates = 0;
% Register the sample times.
% [0 offset] : Continuous sample time
% [positive_num offset] : Discrete sample time
%
% [-1, 0] : Inherited sample time
% [-2, 0] : Variable sample time
block.SampleTimes = [0.01 0];
%
% Register the various block methods with names of the functions mathcing
% appropriately with the steps
% Remember that DWork variables can only be set inside a
% PostPropagationSetup method. So, we need to register that
block.RegBlockMethod('PostPropagationSetup',@DoPostPropSetup);
block.RegBlockMethod('InitializeConditions',@InitConditions);
block.RegBlockMethod('Update', @Update);
% Commenting this out as no output is required
%block.RegBlockMethod('Outputs' ,@Output)
% Code for SimState: idea of splitting simulations into stages and doing
% that by using the end x of one stages as the x0 of the following stage. Look up the
% documentation for more information on this
block.SimStateCompliance = 'DefaultSimState';
function DoPostPropSetup(block)
% Setup Dwork
% The PostPropagationSetup method initializes the DWork vector that stores the state
% DWork vectors are blocks of memory that an S-function asks the Simulink engine to allocate to each instance of the S-function in a model.
% If multiple instances of your S-function can occur in a model, your S-function must use DWork vectors instead of global or static memory to store
% instance-specific values of S-function variables. Otherwise, your S-function runs the risk of one instance overwriting data needed by another instance,
%causing a simulation to fail or produce incorrect results. The ability to keep track of multiple instances of an S-function is called reentrancy
block.NumDworks = 1;
% The (1) indicates this as the first state. You could add other states buy
% subscripting (2), (3) and so on. Note that you have to do book-keeping
% of these states so that you do not mix up-hence good to write some
% documentation.
block.Dwork(1).Name = 'x0';
% DWork is a vector and so its dimension must be a positive integer
block.Dwork(1).Dimensions = 2;
block.Dwork(1).DatatypeID = 0;
block.Dwork(1).Complexity = 'Real';
block.Dwork(1).UsedAsDiscState = false;
function InitConditions(block)
% Initialize Dwork
%block.Dwork(1).Data = (block.DialogPrm(1).Data)';
% Visualization in a MATLAB GUI
% Initialization code. Note that the GUI is being initialized as part of
% the model execution. If you close the GUI during the Simulink model
% execution, you will see a new GUI but this GUI will vanish because the
% Simulink engine has no way of knowing that you undid what you set up
% during the initialization step. Here is an enhanecment to think of:
% Prevent the user from closing the GUI as long as the simulation is
% running. How would you incorporate that?
% Also, observe that the GUI code is not very sophisticated. The objects
% that make this GUI are not connected in a rigid fashion to one figure
% window. If there were multiple plotting functions inside your Simulink
% model, this could cause a lot of overlap. Enhancement to think of: How to
% design more robust GUIs so that they are delinked from each other?
world = vrworld('conveyor.wrl');
open(world);
fig = vrfigure(world);
% get the path to the wrl file with marker PROTOs
pathtomarkers = which('vr_markers.wrl');
% use the tetrahedron shape
MarkerName = 'Marker_Sphere';
% create an EXTERNPROTO with specified marker
try
addexternproto(world, pathtomarkers, MarkerName);
catch ME
% if required PROTO is already contained don't throw an exception
if ~strcmpi(ME.identifier, 'VR:protoexists')
throwAsCaller(ME);
end
end
function Update(block)
persistent markerTime;
MarkerName = 'Marker_Sphere';
% You want to hold the current input as the state(memory) for the next
% computation because you need it for the line command
world = vrworld('conveyor.wrl');
conveyor= vrnode(world, 'conveyor');
conveyor.translation=[block.InputPort(2).Data 0 0];
if (block.InputPort(1).Data(1)>block.Dwork(1).Data(1))
newMarker = vrnode(world, sprintf('%s_%d', 'Marker', block.InputPort(1).Data(1)), MarkerName);
newMarker.markerTranslation = [-20 0 0];
newMarker.markerScale=[0.1 0.1 0.1];
%color table R G B
%BLACK 0 0 0
%RED 255 0 0
%ORAGERED 255 69 0
%darkorange1 255 127 0
%DARKORANGE 255 140 0
%ORANGE 255 165 0
%GOLD 255 215 0
%YELLOW 255 255 0
%LIGHTYELLOW 255 255 224
%IVORY 255 255 240
%WHITE 255 255 255
a=[0 0 0];
j=block.InputPort(1).Data(2);
a(1,j)=1;
newMarker.markerColor=a;
markerTime(block.InputPort(1).Data(1),1)=block.InputPort(2).Data;
end
for i=1:(block.Dwork(1).Data(1))
newMarker = vrnode(world,['Marker_' num2str(i)]);
newMarker.markerTranslation = [10*((block.InputPort(2).Data)-markerTime(i,1))-20 0 0];
end
vrdrawnow;
block.Dwork(1).Data = block.InputPort(1).Data;
% No output required here
%function Output(block)
% block.OutputPort(1).Data = block.Dwork(1).Data;