classdef MassSpringDamperApp < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        MassSpringDamperUIFigure        matlab.ui.Figure
        GridLayout0                     matlab.ui.container.GridLayout
        GridLayout11                    matlab.ui.container.GridLayout
        Panel112                        matlab.ui.container.Panel
        GridLayout112                   matlab.ui.container.GridLayout
        StiffnessNmSpinnerLabel         matlab.ui.control.Label
        StiffnessSpinner                matlab.ui.control.Spinner
        MassKgLabel                     matlab.ui.control.Label
        MassSpinner                     matlab.ui.control.Spinner
        DampingNmsSpinnerLabel          matlab.ui.control.Label
        DampingSpinner                  matlab.ui.control.Spinner
        InitialPositionmEditFieldLabel  matlab.ui.control.Label
        InitialPositionEditField        matlab.ui.control.NumericEditField
        Panel                           matlab.ui.container.Panel
        GridLayout115                   matlab.ui.container.GridLayout
        StopTimesSpinnerLabel           matlab.ui.control.Label
        StopTimeSpinner                 matlab.ui.control.Spinner
        SimulateButton                  matlab.ui.control.Button
        InputForceShapeDropDown         matlab.ui.control.DropDown
        InputForceMagnitudeSpinner      matlab.ui.control.Spinner
        InputForceLabel                 matlab.ui.control.Label
        Image                           matlab.ui.control.Image
        GridLayout12                    matlab.ui.container.GridLayout
        PositionUIAxes                  matlab.ui.control.UIAxes
        VelocityUIAxes                  matlab.ui.control.UIAxes
        InputForceUIAxes                matlab.ui.control.UIAxes
    end

    
    methods (Access = private)
        
        function extInp = externalInput(app)
            Tstop = app.StopTimeSpinner.Value;
            T0 = Tstop/20;
            F = app.InputForceMagnitudeSpinner.Value;
            
            switch (app.InputForceShapeDropDown.Value)
                case 'Gate'
                    tv = [0 T0 T0 2*T0 2*T0 Tstop]';
                    uv = [0  0  F    F    0     0]';
                case 'Step'
                    tv = [0 T0 T0 Tstop]';
                    uv = [0  0  F     F]';
                case 'Ramp'
                    tv = [0 T0 Tstop]';
                    uv = [0  0     F]';
                case 'Bumpy'
                    tv = [0 linspace(T0,Tstop-T0,10) Tstop]';
                    uv = [0 2*F*(rand(1,10)-0.5) 0]';
                otherwise
                    error('invalid input signal type');
            end
            plot(app.InputForceUIAxes, tv, uv, 'Marker','.');
            umin = min(uv); umax = max(uv); urng = 0.1*(umax-umin);
            app.InputForceUIAxes.YLim = [umin-urng umax+urng];
            extInp = [tv uv];
        end % externalInput
        
        function toggleUIC(app, state, label)
            % toggle the state of the uicontrols in the app
            uics = findall(app.MassSpringDamperUIFigure);
            for idx = 1:length(uics)
               uic = uics(idx);
               if isprop(uic,'Enable')
                   uic.Enable = state;
               end
            end
            % update the simulate button text
            app.SimulateButton.Text = label;
            drawnow; % flush the ui events queue
        end % toggleUIC
    end

    % Callbacks that handle component events
    methods (Access = private)

        % Code that executes after component creation
        function startupFcn(app)
            % load simulink to pay the one time cost of loading libraries
            % % during app startup.
            start_simulink;
        end

        % Button pushed function: SimulateButton
        function SimulateButtonPushed(app, event)
            try
                app.toggleUIC('off', 'Simulating ...');

                % create the simulation input
                simInp = Simulink.SimulationInput('MassSpringDamperModel');

                % set the parameters for this run
                simInp = simInp.setVariable('k',app.StiffnessSpinner.Value);
                simInp = simInp.setVariable('m',app.MassSpinner.Value);
                simInp = simInp.setVariable('b',app.DampingSpinner.Value);
                simInp = simInp.setVariable('x0',app.InitialPositionEditField.Value);
                
                % set the model parameters for this run
                stopTimeStr = num2str(app.StopTimeSpinner.Value);
                simInp = simInp.setModelParameter('StopTime', stopTimeStr);

                % set the external input for this run
                simInp.ExternalInput = app.externalInput();

                % configure simInp for deployment
                simInp = simulink.compiler.configureForDeployment(simInp);
                % DEBUG TIP: Comment out the line above for faster debug
                % iterations when runnng this app in the MATLAB desktop.
                
                % run
                simOut = sim(simInp);
                
                % extract and plot the results
                t = simOut.y.time;
                yp = simOut.y.signals(1).values;
                yv = simOut.y.signals(2).values;
                plot(app.PositionUIAxes, t, yp);
                plot(app.VelocityUIAxes, t, yv);
            catch ME
                errordlg(ME.message);    
            end
            app.toggleUIC('on', 'Simulate');
        end
    end

    % Component initialization
    methods (Access = private)

        % Create UIFigure and components
        function createComponents(app)

            % Create MassSpringDamperUIFigure and hide until all components are created
            app.MassSpringDamperUIFigure = uifigure('Visible', 'off');
            app.MassSpringDamperUIFigure.Position = [100 100 640 480];
            app.MassSpringDamperUIFigure.Name = 'Mass Spring Damper';

            % Create GridLayout0
            app.GridLayout0 = uigridlayout(app.MassSpringDamperUIFigure);
            app.GridLayout0.ColumnWidth = {305, '1x'};
            app.GridLayout0.RowHeight = {'1x'};

            % Create GridLayout11
            app.GridLayout11 = uigridlayout(app.GridLayout0);
            app.GridLayout11.ColumnWidth = {'1x'};
            app.GridLayout11.RowHeight = {'1x', 'fit', 'fit'};
            app.GridLayout11.Layout.Row = 1;
            app.GridLayout11.Layout.Column = 1;

            % Create Panel112
            app.Panel112 = uipanel(app.GridLayout11);
            app.Panel112.Layout.Row = 2;
            app.Panel112.Layout.Column = 1;

            % Create GridLayout112
            app.GridLayout112 = uigridlayout(app.Panel112);
            app.GridLayout112.RowHeight = {22, 22, 22, 26};

            % Create StiffnessNmSpinnerLabel
            app.StiffnessNmSpinnerLabel = uilabel(app.GridLayout112);
            app.StiffnessNmSpinnerLabel.HorizontalAlignment = 'right';
            app.StiffnessNmSpinnerLabel.Layout.Row = 2;
            app.StiffnessNmSpinnerLabel.Layout.Column = 1;
            app.StiffnessNmSpinnerLabel.Text = 'Stiffness (N/m)';

            % Create StiffnessSpinner
            app.StiffnessSpinner = uispinner(app.GridLayout112);
            app.StiffnessSpinner.Limits = [0 Inf];
            app.StiffnessSpinner.Layout.Row = 2;
            app.StiffnessSpinner.Layout.Column = 2;
            app.StiffnessSpinner.Value = 128;

            % Create MassKgLabel
            app.MassKgLabel = uilabel(app.GridLayout112);
            app.MassKgLabel.HorizontalAlignment = 'right';
            app.MassKgLabel.Layout.Row = 1;
            app.MassKgLabel.Layout.Column = 1;
            app.MassKgLabel.Text = 'Mass (Kg)';

            % Create MassSpinner
            app.MassSpinner = uispinner(app.GridLayout112);
            app.MassSpinner.Layout.Row = 1;
            app.MassSpinner.Layout.Column = 2;
            app.MassSpinner.Value = 2;

            % Create DampingNmsSpinnerLabel
            app.DampingNmsSpinnerLabel = uilabel(app.GridLayout112);
            app.DampingNmsSpinnerLabel.HorizontalAlignment = 'right';
            app.DampingNmsSpinnerLabel.Layout.Row = 3;
            app.DampingNmsSpinnerLabel.Layout.Column = 1;
            app.DampingNmsSpinnerLabel.Text = 'Damping (N/m/s)';

            % Create DampingSpinner
            app.DampingSpinner = uispinner(app.GridLayout112);
            app.DampingSpinner.Limits = [0 Inf];
            app.DampingSpinner.Layout.Row = 3;
            app.DampingSpinner.Layout.Column = 2;
            app.DampingSpinner.Value = 3;

            % Create InitialPositionmEditFieldLabel
            app.InitialPositionmEditFieldLabel = uilabel(app.GridLayout112);
            app.InitialPositionmEditFieldLabel.HorizontalAlignment = 'right';
            app.InitialPositionmEditFieldLabel.Layout.Row = 4;
            app.InitialPositionmEditFieldLabel.Layout.Column = 1;
            app.InitialPositionmEditFieldLabel.Text = 'Initial Position (m)';

            % Create InitialPositionEditField
            app.InitialPositionEditField = uieditfield(app.GridLayout112, 'numeric');
            app.InitialPositionEditField.Layout.Row = 4;
            app.InitialPositionEditField.Layout.Column = 2;

            % Create Panel
            app.Panel = uipanel(app.GridLayout11);
            app.Panel.Layout.Row = 3;
            app.Panel.Layout.Column = 1;

            % Create GridLayout115
            app.GridLayout115 = uigridlayout(app.Panel);
            app.GridLayout115.ColumnWidth = {'1x', '1x', '1x'};
            app.GridLayout115.RowHeight = {26, 26};

            % Create StopTimesSpinnerLabel
            app.StopTimesSpinnerLabel = uilabel(app.GridLayout115);
            app.StopTimesSpinnerLabel.HorizontalAlignment = 'right';
            app.StopTimesSpinnerLabel.Layout.Row = 2;
            app.StopTimesSpinnerLabel.Layout.Column = 1;
            app.StopTimesSpinnerLabel.Text = 'Stop Time (s)';

            % Create StopTimeSpinner
            app.StopTimeSpinner = uispinner(app.GridLayout115);
            app.StopTimeSpinner.Step = 5;
            app.StopTimeSpinner.Limits = [0 Inf];
            app.StopTimeSpinner.Layout.Row = 2;
            app.StopTimeSpinner.Layout.Column = 2;
            app.StopTimeSpinner.Value = 20;

            % Create SimulateButton
            app.SimulateButton = uibutton(app.GridLayout115, 'push');
            app.SimulateButton.ButtonPushedFcn = createCallbackFcn(app, @SimulateButtonPushed, true);
            app.SimulateButton.BackgroundColor = [0.4667 0.6745 0.1882];
            app.SimulateButton.FontWeight = 'bold';
            app.SimulateButton.Layout.Row = 2;
            app.SimulateButton.Layout.Column = 3;
            app.SimulateButton.Text = 'Simulate';

            % Create InputForceShapeDropDown
            app.InputForceShapeDropDown = uidropdown(app.GridLayout115);
            app.InputForceShapeDropDown.Items = {'Gate', 'Step', 'Ramp', 'Bumpy'};
            app.InputForceShapeDropDown.Layout.Row = 1;
            app.InputForceShapeDropDown.Layout.Column = 3;
            app.InputForceShapeDropDown.Value = 'Gate';

            % Create InputForceMagnitudeSpinner
            app.InputForceMagnitudeSpinner = uispinner(app.GridLayout115);
            app.InputForceMagnitudeSpinner.Step = 5;
            app.InputForceMagnitudeSpinner.Layout.Row = 1;
            app.InputForceMagnitudeSpinner.Layout.Column = 2;
            app.InputForceMagnitudeSpinner.Value = 10;

            % Create InputForceLabel
            app.InputForceLabel = uilabel(app.GridLayout115);
            app.InputForceLabel.HorizontalAlignment = 'right';
            app.InputForceLabel.Layout.Row = 1;
            app.InputForceLabel.Layout.Column = 1;
            app.InputForceLabel.Text = 'Input Force (N)';

            % Create Image
            app.Image = uiimage(app.GridLayout11);
            app.Image.ScaleMethod = 'scaleup';
            app.Image.Layout.Row = 1;
            app.Image.Layout.Column = 1;
            app.Image.ImageSource = 'MassSpringDamper.png';

            % Create GridLayout12
            app.GridLayout12 = uigridlayout(app.GridLayout0);
            app.GridLayout12.ColumnWidth = {'1x'};
            app.GridLayout12.RowHeight = {'1x', '1x', '1x'};
            app.GridLayout12.Layout.Row = 1;
            app.GridLayout12.Layout.Column = 2;

            % Create PositionUIAxes
            app.PositionUIAxes = uiaxes(app.GridLayout12);
            title(app.PositionUIAxes, '')
            xlabel(app.PositionUIAxes, '')
            ylabel(app.PositionUIAxes, 'Position')
            app.PositionUIAxes.Box = 'on';
            app.PositionUIAxes.Layout.Row = 2;
            app.PositionUIAxes.Layout.Column = 1;

            % Create VelocityUIAxes
            app.VelocityUIAxes = uiaxes(app.GridLayout12);
            title(app.VelocityUIAxes, '')
            xlabel(app.VelocityUIAxes, '')
            ylabel(app.VelocityUIAxes, 'Velocity')
            app.VelocityUIAxes.Box = 'on';
            app.VelocityUIAxes.Layout.Row = 3;
            app.VelocityUIAxes.Layout.Column = 1;

            % Create InputForceUIAxes
            app.InputForceUIAxes = uiaxes(app.GridLayout12);
            title(app.InputForceUIAxes, '')
            xlabel(app.InputForceUIAxes, '')
            ylabel(app.InputForceUIAxes, 'Input Force')
            app.InputForceUIAxes.Box = 'on';
            app.InputForceUIAxes.Layout.Row = 1;
            app.InputForceUIAxes.Layout.Column = 1;

            % Show the figure after all components are created
            app.MassSpringDamperUIFigure.Visible = 'on';
        end
    end

    % App creation and deletion
    methods (Access = public)

        % Construct app
        function app = MassSpringDamperApp

            % Create UIFigure and components
            createComponents(app)

            % Register the app with App Designer
            registerApp(app, app.MassSpringDamperUIFigure)

            % Execute the startup function
            runStartupFcn(app, @startupFcn)

            if nargout == 0
                clear app
            end
        end

        % Code that executes before app deletion
        function delete(app)

            % Delete UIFigure when app is deleted
            delete(app.MassSpringDamperUIFigure)
        end
    end
end