%% Integrate Generated Code by Using Cross-Release Workflow
% This example shows how to reuse previously generated code by creating a
% cross-release software-in-the-loop (SIL) block and incorporating the
% block within an integration model. For more information about the
% workflow, see <docid:ecoder_ug.bvdzn2t Cross-Release Code Integration>.
%
% Copyright 2017-2020 The MathWorks, Inc.

%% Generate Code from a Model
% In the cross-release code integration workflow, you create a
% software-in-the-loop (SIL) or processor-in-the-loop (PIL) block from code
% that you previously generated.
% 
% This example uses generated code from the current release.
model = 'rtwdemo_crossrelease_counter';
close_system(model,0)
load_system(model)
set_param(model, 'SimulationCommand', 'update');
open_system(model)
rtwbuild(model);

%%
% Identify the build folder that contains the generated code.
buildFolder = RTW.getBuildDir(model).BuildDirectory;

%%
% Identify the folder that contains code for shared utilities associated
% with the generated code.
previousSharedCodeFolder = RTW.getBuildDir(model).SharedUtilsTgtDir;

%%
% To reuse generated code from a previous release:
%
% # On the *Simulation* tab, in the *File* section, select *Save > Previous Version*.
% # In the *Save as type field*, specify the release and model type.
% # Click *Save*.
% # Use the previous release to open the saved model, and then generate
% code.
% # Set the value of |buildFolder| to the location of the code generated in
% the previous release.
% # Set the value of |previousSharedCodeFolder| to the location of the shared
% utilities generated in the previous release.

%% Manage Shared Code
% Add the generated shared source code files to a repository folder that
% is used by the integration model.
sharedCodeRepo = 'SharedCodeRepo';
mkdir(sharedCodeRepo);
sharedCodeUpdate(previousSharedCodeFolder, sharedCodeRepo, 'Interactive', false);

%% Import Code
% Load the integration model.
integrationModel = 'rtwdemo_crossrelease_integration';
close_system(integrationModel, 0);
load_system(integrationModel);

%%
% Modify the Simulink configuration set so that it references the existing
% shared code library.
cs = getActiveConfigSet(integrationModel);
set_param(cs, 'ExistingSharedCode', fullfile(pwd, sharedCodeRepo));

%%
% Create the cross-release SIL block.
blockHandle = crossReleaseImport(buildFolder, cs, 'SimulationMode', 'SIL');

%% Incorporate Cross-Release SIL Block into Integration Model
% To replace a block in the integration model with the cross-release block,
% use |pil_block_replace|. This function preserves the block size, line
% connections, and priority.
srcBlock = getfullname(blockHandle);
dstBlock = [integrationModel, '/', 'Counter'];
pil_block_replace(srcBlock, dstBlock)
open_system(integrationModel)

%% Simulate Integration Model
% Run a simulation of the integration model.
sim(integrationModel)

%% Tune Parameters
% The source model for the cross-release SIL block references two tunable
% parameters, which are controlled by Simulink.Parameter objects in the
% base workspace. Use these parameters to modify the behavior of the SIL 
% simulation.
countUpper.Value = 30;
countLower.Value = 20;
yout_retuned = sim(integrationModel, 'ReturnWorkspaceOutputs', 'on');

%% Configure Storage Class for Signals in Integration Model
% Configure:
%
% * Signal names in the integration model to match the names used in the
% imported code.
% * Complementary storage classes.
%
% In this case, the |ticks| input and |count| output are implemented
% through the |ImportedExtern| storage class in the imported code.
% If the storage class for the signals connected to the input and output
% ports of the cross-release block in the integration model
% is |ExportedGlobal|, the integration model must provide the definitions 
% for the variables.
%
% If the signal names do not match, the integration model generates
% additional code to copy data between the signal implemented by 
% the integration model and the signal implemented by the imported code.
hLines = get_param(dstBlock, 'LineHandles');

% Use the code mappings API to configure the storage class on the signals.
cm = coder.mapping.api.get(integrationModel);
ticksPortHandle = get(hLines.Inport(1), 'SrcPortHandle');
set(ticksPortHandle, 'Name', 'ticks');
addSignal(cm, ticksPortHandle);
setSignal(cm, ticksPortHandle, 'StorageClass', 'ExportedGlobal');

countPortHandle = get(hLines.Outport(1), 'SrcPortHandle');
set(countPortHandle, 'Name', 'count');
addSignal(cm, countPortHandle);
setSignal(cm, countPortHandle, 'StorageClass', 'ExportedGlobal');


%% Configure Storage Class for Parameters and Data Store Memory in Integration Model
% The parameters and data stores are implemented through the
% |ImportedExtern| storage class in the imported code. If you configure the 
% parameters and data stores to use the |ExportedGlobal| storage class, the
% integration model must provide the definitions for the variables.
resetSignal.CoderInfo.StorageClass = 'ExportedGlobal';
countLower.CoderInfo.StorageClass = 'ExportedGlobal';
countUpper.CoderInfo.StorageClass = 'ExportedGlobal';

%% Generate Code from Integration Model
% Once the required shared code is in the shared code repository, remove
% the previously generated shared utilities folder.
if isfolder(RTW.getBuildDir(integrationModel).SharedUtilsTgtDir)
    rmdir(RTW.getBuildDir(integrationModel).SharedUtilsTgtDir, 's');
end

%%
% Delete the scope and connecting line that do not affect code
% generation.
scopeBlock = [integrationModel, '/', 'Scope'];
hScopeLines = get_param(scopeBlock, 'LineHandles');
hScopeLine = hScopeLines.Inport(1);
assert(strcmp(get(hScopeLine, 'SegmentType'), 'branch'));
delete_line(hScopeLine);
delete_block(scopeBlock);

%%
% Generate code.
rtwbuild(integrationModel);

%% Examine Call to Imported Code
% To examine the cross-release block code within the integration model
% code, use the |rtwtrace| utility.
rtwtrace(dstBlock);

%% Test Code Generated from Integration Model
% Run a top-model software-in-the-loop (SIL) simulation of the integration
% model.  The simulation runs the code generated from the integration
% model, which calls the imported code.
%
% Log the simulation output in the workspace.
set_param(integrationModel, 'SimulationMode', 'software-in-the-loop (sil)');
yout_SIL = sim(integrationModel, 'ReturnWorkspaceOutputs', 'on');
plot(yout_SIL.yout{1}.Values);

%% Compare Simulation Outputs
% Compare the outputs from the simulation where only the imported code ran
% in SIL mode with the outputs from the simulation where the integration
% model ran as a top-model in SIL mode.
max(abs(yout_SIL.yout{1}.Values.Data - yout_retuned.yout{1}.Values.Data))
