%% Generate Generic C/C++ for Sequence-to-Sequence Deep Learning Simulink Models
% This example shows how to generate a generic C/C++ executable that does not depend on any third-party deep learning libraries from a deep learning Simulink® model. The example predicts the remaining useful life (RUL) of a turbofan engine, measured in cycles at each step of an input time series that represents data from various sensors in an engine.
% For more information on generating C/C++ code from MATLAB®, see Generate Generic C/C++ Code for Sequence-to-Sequence Regression That Uses Deep Learning.
%
%% Pretrained RUL Network
% This example uses a pretrained LSTM network to predict the remaining useful life of an engine, measured in cycles. The LSTM network consists of an LSTM layer with 200 hidden units, followed by a fully connected layer of size 50 and a dropout layer with dropout probability 0.5.The network was trained using the Turbofan Engine Degradation Simulation Data Set as described in [1]. The training data contains simulated time series data for 100 engines. Each sequence varies in length and corresponds to a full run to failure (RTF) instance. The test data contains 100 partial sequences and corresponding values of the remaining useful life at the end of each sequence.  For more information on training the network, see the example Sequence-to-Sequence Regression Using Deep Learning.

net = coder.loadDeepLearningNetwork('rulNetwork.mat')

%
%% Prepare Test Data
% Load the TurboFanRULValidate MAT-file that contains the Turbofan Engine Degradation Validation Data Set. This MAT-file stores the variable XValidate that contains 100 sample observations representing sensor readings that you use to test the Simulink model.

load('TurboFanRULValidate.mat')

% To make the input validation data compatible with Simulink code generation, the sequence lengths for each of the independent 100 observations are zero-padded to create uniformly-sized, 17-by-303 input arrays.

sequenceLengths = cellfun(@length,XValidate,'UniformOutput',true);
maxSequenceLen = max(sequenceLengths);
padFcn = @(x) [x,zeros(size(x,1),maxSequenceLen-size(x,2))];             
XValidatePad = cellfun(padFcn,XValidate,'UniformOutput',false);

% The padded values are then converted to a 17-by-303-100 numeric array.  To import this data into the Simulink model, specify a structure variable containing the data values and an empty time vector. During simulation, the input for the first time step is read from the first 17-by-303 element of the array. The value for the second time step is read from the second element, and so on, for a total of 100 steps.

simin.time = [];
simin.signals.values = cell2mat(reshape(XValidatePad,1,1,[]));
simin.signals.dimensions = size(XValidatePad{1});

%
%% Simulink Model for Prediction
% The Simulink model for predicts the remaining useful life of a turbofan engine is shown. The model uses the Predict block from the Deep Neural Networks library that imports the trained network from a rulNetwork MAT-file. Additionally, the Mini-batch size parameter of the block is set to 1.

model = 'rulPredict.slx';
open_system(model)

%
%
%% Run the Simulation
% To validate the Simulink model, run the simulation.

sim(model);

% The output YPred of the Simulink model contains the predicted remaining useful life values from the network. This output is first trimmed to remove the results from the zero-padded values and then converted to a cell array.

YPred_cell = squeeze(mat2cell(YPred,1,maxSequenceLen,ones(1,100)));

for t = 1:length(sequenceLengths)   
    YPred_cell{t}(:,sequenceLengths(t) + 1:end) = [];
end 

% Plot the predicted remaining useful life (RUL) values for four randomly-selected observations and compare them to the validation data.

observationIdx = randperm(100,4);
rulExamplePlots(observationIdx,YValidate,YPred_cell);

%
%% Configure Model for Code Generation
% To generate C/C++ code that does not depend on any third-party deep learning libraries and build an executable, select the generic real-time (ERT) target from Embedded Coder™.

cs = getActiveConfigSet('rulPredict');
switchTarget(cs,'ert.tlc',[]);

% Configure code generation specific parameters. Setting MAT-file logging enables the generated code to redirect simulation data to a MAT-file.

cs.set_param('TargetLang','C++'); 
cs.set_param('ObjectivePriorities','Execution efficiency');
cs.set_param('DLTargetLibrary','None');
cs.set_param('GenerateReport','on');
cs.set_param('CodeInterfacePackaging','Nonreusable function'); 
cs.set_param('MatFileLogging', 'on');

%
%% Generate and Build the Simulink Model
% Generate and build the Simulink model by using the slbuild command. The code generator places the files in a build folder, a subfolder named rulPredict_ert_rtw under your current working folder.

evalc("slbuild('rulPredict')");

%
%% Run the Generated Executable
% Run the generated executable. After successful execution, the rulPredict executable creates a rulPredict MAT-file in the current working folder.

if ispc
    status = system('rulPredict.exe');
else
    status = system('./rulPredict');
end 

%

load('rulPredict.mat')

% The output rt_YPred contains the predicted remaining useful life values from the network. This output is first trimmed to remove the results from the zero-padded values and then converted to a cell array.

rt_YPred_cell = squeeze(mat2cell(rt_YPred,1,maxSequenceLen,ones(1,100)));

for t = 1:length(sequenceLengths)   
    rt_YPred_cell{t}(:,sequenceLengths(t) + 1:end) = [];
end 

% Plot the predicted remaining useful life (RUL) values for four randomly-selected observations and compare them to the validation data.

rulExamplePlots(observationIdx,YValidate,rt_YPred_cell);

%
%% Supporting Functions

function rulExamplePlots(observationIdx,YTest,YPred)

figure
for i = 1:numel(observationIdx)
    subplot(2,2,i)

    plot(YTest{observationIdx(i)},'--')
    hold on
    plot(YPred{observationIdx(i)},'.-')
    hold off

    ylim([0 175])
    title("Test Observation " + observationIdx(i))
    xlabel("Time Step")
    ylabel("RUL")
end
legend(["Test Data" "Predicted"],'Location','southeast')

end

%% References
% Saxena, Abhinav, Kai Goebel, Don Simon, and Neil Eklund. "Damage propagation modeling for aircraft engine run-to-failure simulation." In Prognostics and Health Management, 2008. PHM 2008. International Conference on, pp. 1-9. IEEE, 2008.
% Copyright 2021 The MathWorks, Inc.