%% Deploy Mass Spring Damper App with LiveIO
% In this example, we use an app with LiveIO functionality, that is prepared in the App Designer. You can then deploy it with Simulink® Compiler™.
%% Open and Explore the Project File
% This example uses a Simulink project that contains all the files required to run this example. The project contains a Simulink model of a mass springer damper system. The mass-spring-damper model consists of discrete mass nodes distributed throughout an object and interconnected via a network of springs and dampers. This model is well-suited for modelling object with complex material properties such as non-linearity and elasticity. In this example we use the mass spring damper system. The system is parametrized by mass (m), spring stiffness (k), damping (b) and the initial position (x0). The input to the system is the applied force.
% To explore this model with different values of the tunable parameters, create the following model workspace variables:
% Mass - m.
% Spring stiffness - k.
% Damping - b.
% Initial position - xo.
%
% To create the model workspace variables, go to the Modelling tab and select Model Workspace in the Data Repositories in the Design section. Use the Add MATLAB Variables icon to add the above four variables. Add the appropriate initial values, for example, 3, 128, 2 and 0.5 respectively.
% simulink.compiler.example.MassSpringDamperWithLiveIO
%% Create the App in App Designer
% se the MATLAB APP Designer to create an app to simulate the model with different parameter values and input signals. To learn more about how to create an app using the App Designer, see Create and Run a Simple App Using App Designer Use the MassSpringDamperAppWithLiveIO.mlapp file to use the app.
% MassSpringDamperAppWithLiveIO
%
%
% App Details
% Open theMassSpringDamperAppWithLiveIO.mlapp file. You can view the code written to create this app in Code View section of the App Designer. The essential part of this app is the behavior of the Start button. It has the following salient parts: creating the SimulationInput object, configuring it for deployment, using simulation callbacks to read the output port data and plot the data at each time step. These three functions allow you to see the live results of the simulation in the deployed app.
% Creating the Simulink.SimulationInput Object
% The function createSimulationInput defines an empty Simulink.SimulationInput object for the model. Use this Simulink.SimulationInput object to set simulation callbacks and variables for the model.
% The simulation callback functions are used to register the callbacks. The simulink.compiler.setPostStepFcn function registers a callback that is invoked after every simulation step. The simulink.compiler.setExternalInputsFcn registers a callback that dynamically processes the values for every input port at root level of a model during simulation. The simulink.compiler.setExternalOuputsFcn registers a callback that dynamically processes the values for every output port at root level of a model during simulation.
% Use the setVariable method of the Simulink.SimulationInput object to provide the parameter values to the app. Values for the simulation are obtained from the edit fields of the UI of the app. To deploy the app, use the simulink.compiler.configureForDeployment function. (Comment the line of code that calls simulink.compiler.configureForDeployment function for faster debugging.)
%
% function simInp = createSimulationInput(app)
%             % Create an empty SimulationInput object
%             simInp = Simulink.SimulationInput(app.modelName);
%
%             % Specify the simulation callbacks
%             simInp = simulink.compiler.setPostStepFcn(simInp, @app.postStepFcn);
%             simInp = simulink.compiler.setExternalInputsFcn(simInp, @app.getInputs);
%             simInp = simulink.compiler.setExternalOutputsFcn(simInp, @app.processOutputs);
%
%             %--------------------------------------------------------------
%             % Load the parameters values from the ui edit fields
%             simInp = simInp.setVariable('k',app.StiffnessUIC.Value);
%             simInp = simInp.setVariable('m',app.MassUIC.Value);
%             simInp = simInp.setVariable('b',app.DampingUIC.Value);
%             simInp = simInp.setVariable('x0',app.X0UIC.Value);
%
%             % Since sim will run forever, turn off (or limit) data logging
%             simInp = simInp.setModelParameter('StopTime','inf');
%             simInp = simInp.setModelParameter('SaveOutput','off');
%             %--------------------------------------------------------------
%
%             % Configure simInp for deployment
%             % DEBUG TIP: Comment out the line below for
%             % faster/easier debugging when runnng in MATLAB
%             simInp = simulink.compiler.configureForDeployment(simInp);
%         end % createSimulationInput
% Simulation Callback Functions
% The simulation callback functions register callbacks that allow you to read values from the output ports and to wrtie values to the root input ports. These functions register callbacks at every simulation time step which allows you to view live results of the simulation.
% The getInputs Callback
% The getInputs callback function is used to get inputs from the root inport ports.
% function data = getInputs(app, ipIdx, time)
%             %--------------------------------------------------------------
%             data = 0;
%             if app.inputSignalGenerator.count == 0
%                 % generate uniform random value between [0, 1]
%                 ur01 = rand(app.inputSignalGenerator.randStream);
%
%                 % input force
%                 data = 1/(2*(0.5-ur01));
%                 if abs(data) > app.MaxMagnitudeSpinner.Value
%                     data = sign(data)*app.MaxMagnitudeSpinner.Value;
%                 end
%                 app.inputSignalGenerator.value = data;
%
%                 % Use Rayleigh distribution initialize the count for how
%                 % long to hold the input at this value
%                 sigma = sqrt(2/pi)*app.MeanTimeBetweenImpulsesSpinner.Value;
%                 app.inputSignalGenerator.count = ceil(sigma*sqrt(-2*log(ur01)));
%             else
%                 app.inputSignalGenerator.count = app.inputSignalGenerator.count - 1;
%             end
%             %--------------------------------------------------------------
%             tag = string(['ip', num2str(ipIdx)]);
%             app.updateSignalTrace(tag,time,data,'stair');
%         end % getInputs
% The processOutputs Callback
% The simulink.compiler.setExternalOutputsFcn line refers to the function procressOuputs. The procressOuputs callback function that processes the values for every root output port block of model during simulation. The postprocressOuputs function is called once per port and per the sample time of the port. When the procressOuputs function is called, it reads the values for every root outport block and caches those values. The postStepFcn obtains the cached values to update the plot.
% function processOutputs(app, opIdx, time, data)
%             % Called during sim to process the external output port data,
%             % will be called once per port per its sample hit.
%
%             tag = string(['op', num2str(opIdx)]);
%             app.updateSignalTrace(tag,time,data,'');
%         end % processOutputs
% The postStepFcn Callback
% The postStepFcn callback function is invoked after every simulation step. The time argument is the time for the previous simulation step. The postStepFcn function obtains the cached outport block values for every time and passes those values to the clipSignalTrace function to plot the cached values at simulation time.
% function postStepFcn(app, time)
%             % Called during sim after each simulation time step
%             app.updateSimStats(time);
%             if app.status == AppStatus.Starting
%                 % Switch status to running after the first step, enables stop button
%                 app.switchStatus(AppStatus.Running);
%                 app.simStats.WallClockTimeAfterFirstStep = tic;
%             end
%
%             if app.processPendingParameterChanges
%                 vars(1) = Simulink.Simulation.Variable('m', app.MassUIC.Value);
%                 vars(2) = Simulink.Simulation.Variable('b',app.DampingUIC.Value);
%                 vars(3) = Simulink.Simulation.Variable('k',app.StiffnessUIC.Value);
%                 simulink.compiler.modifyParameters(app.modelName, vars);
%                 app.processPendingParameterChanges = false;
%             end
%
%             app.clipSignalTraces();
%
%             drawnow limitrate;
%         end % postStepFcn
%
%% Test in App Designer
% Before deploying the application, ensure that the app runs in the App Designer. Click Start to verify that the application works by simulating the model for different values.
%
%% Compile App for Deployment
% You can use App Designer to compile and deploy the app. You can also use deploytool. For more information on compiling and deploying with App Designer, see Develop Apps Using App Designer, Web Apps and Application Compiler.
% To compile the app in this example, use the mcc command followed by the app name.
% mcc -m MassSpringDamperAppWithLiveIO
% Copyright 2021 The MathWorks, Inc.