%% Establish Data Ownership in a Model Hierarchy
% This example shows how to establish ownership of global data
% in the code generated from referenced models.  
%
% You can create a global variable in the generated code by applying a 
% storage class to a data element in a referenced model (see <docid:ecoder_ug.f1068700 
% Apply Storage Classes to Individual Signal, State, and Parameter Data Elements>).
% Under certain conditions, the code generator places the variable definition with the
% code generated from the top model in the hierarchy. This default
% placement can make it more difficult to determine which model is
% responsible for the data and to manage code changes in a team-based
% development environment. 
%
% To establish ownership of a global variable by placing the definition
% with the code generated from the relevant model, apply a storage
% class such as |ExportToFile| and specify the *Owner* custom attribute. 
% Alternatively, for parameter objects such as |Simulink.Parameter| that 
% you use in only one model, you can establish ownership by
% storing the object in a model workspace instead of the base workspace or
% a data dictionary. 
%% Explore Example Model
% 1. Copy the script file |prepare_sldemo_fuelsys_dd_ctrl.m| to your current folder.
[~, ~] = copyfile(fullfile(matlabroot,'examples','ecoder','main','prepare_sldemo_fuelsys_dd_ctrl.m'),...
    'prepare_sldemo_fuelsys_dd_ctrl.m','f');
%%
% 2. Run the copy of the script in your current folder. The script opens the model
% |sldemo_fuelsys_dd_controller| and prepares it for this example.
prepare_sldemo_fuelsys_dd_ctrl
open_system('sldemo_fuelsys_dd_controller')
%%
% This controller model contains two models, |airflow_calc| and |fuel_calc|. 
% The two outputs of |airflow_calc|, |est_airflow| and |fb_correction|, 
% are inputs of |fuel_calc|.
%
% 3. Open the |airflow_calc| model.
open_system('airflow_calc')
%%
% 4. On the *Modeling* tab, click *Model Data Editor*.
%
% 5. In the Model Data Editor, set the *Change view* drop-down list to |Code|.
%
% 6. In the |airflow_calc| model, select the |est_airflow| Outport block. 
%
% 7. In the Model Data Editor, inspect the value in the *Storage Class* column. The signal
% that this block represents, |est_airflow|, uses the storage class
% |ExportToFile|. With this setting, the signal appears in the generated code as a global
% variable. The Outport block labeled |fb_correction| also uses this
% setting.
%
% 8. In the Model Data Editor, select the *Parameters* tab and click the
% *Show/refresh additional information* button. The Model Data Editor now
% shows information about workspace variables and objects, such as
% |Simulink.Parameter| objects, that the model uses to set block parameter
% values.
%
% 9. In the *Filter contents* box, enter |numerator|. The |Simulink.Parameter|
% object |numerator_param|, which is in the base workspace, sets the value of the
% *Numerator* parameter in the Discrete Filter block labeled |Throttle
% Transient|. The object uses the storage class |ExportToFile|.
%
% 10. Select the *Signals* tab. In the *Filter contents* box, enter |e0|. 
% The signal |e0|, which is internal to the |airflow_calc| referenced model,
% also uses |ExportToFile|.
% The signal is internal because it is not a root-level input or output of
% the model.
%
% 11. Open the |fuel_calc| model.
open_system('fuel_calc')
%%
% 12. In the Model Data Editor for this model, on the *Inports/Outports* tab,
% set *Change view* to |Code|. The Inport blocks |est_airflow| and |fb_correction| use the same
% storage class, |ExportToFile|, as the corresponding Outport blocks in |airflow_calc|.
% The Outport block labeled |fuel_rate| also uses
% |ExportToFile|.
%% Generate and Inspect Code
% Generate code from the controller model, |sldemo_fuelsys_dd_controller|.
evalc('rtwbuild(''sldemo_fuelsys_dd_controller'')');
%%
% In the code generation report, under *Referenced Models*, click the
% hyperlink to inspect the code generated for |airflow_calc|.
%
% The file |airflow_calc.c| defines the global variable that represents
% the signal |e0|. Because the blocks that write to and read from |e0|
% exist only in |airflow_calc|, the code generator assumes that this
% model owns the signal.
file = fullfile('slprj','ert','airflow_calc','airflow_calc.c');
rtwdemodbtype(file,'/* Definition for custom storage class: ExportToFile */',...
    'real32_T e0;',1,1)
%%
% In the code generation report, return to the code generated for
% |sldemo_fuelsys_dd_controller|.
%
% The file |sldemo_fuelsys_dd_controller.c| defines the other global
% variables.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_fuelsys_dd_controller.c');
rtwdemodbtype(file,...
    '/* Definition for custom storage class: ExportToFile */',...
    'real32_T numerator_param[2] = { 0.01F, -0.01F } ;',1,1);
%%
% Because the signals |est_airflow| and |fb_correction| pass between the
% two models and through the top-level controller model, the code generator assumes that
% |sldemo_fuelsys_dd_controller| owns the signals.
% The code generator makes a similar assumption about |fuel_rate|. Because the
% parameter object |numerator_param| exists in the base workspace, any
% model in the hierarchy can use the object. Therefore, the code generator
% assumes that |sldemo_fuelsys_dd_controller| owns the parameter.
%
% You can configure each shared signal and the parameter object 
% so that the corresponding variable definition
% appears in the code generated for the relevant model.
%% Configure Data Ownership
% In this example, you configure code generation settings so that:
%
% * The code generated for the |airflow_calc| model defines the signals
% that pass between the two models, |est_airflow| and |fb_correction|.
% * The code generated for |airflow_calc| defines the parameter data,
% |numerator_param|.
% * The code generated for |fuel_calc| defines the output signal that it
% calculates, |fuel_rate|.
% 
% 1. In the |airflow_calc| model, on the *Modeling* tab, under *Design*, 
% click *Property Inspector*.
%
% 2. In the Model Data Editor, select the *Inports/Outports* tab.
%
% 3. Select the row that corresponds to the Outport block |est_airflow|.
%    The Property Inspector shows the properties of the Outport block.
%
% 4. In the Property Inspector, under *Code*, set *Owner* to |airflow_calc|.
%    The code generator places the definition of the global variable with the
%    code generated for |airflow_calc|.
%  
% 5. For |fb_correction|, use the Model Data Editor and the Property Inspector 
%    to set *Owner* to |airflow_calc|.
%
% 6. Select the *Parameters* tab.
%
% 7. Find the row that corresponds to the parameter object, |numerator_param|.
%    In the row, double-click the icon in the left column. The Model Explorer
%    opens and displays the properties of |numerator_param|.
%
% 8. In the *Model Hierarchy* pane, expand the *airflow_calc* node so that you
%    can see the subordinate *Model Workspace* node.
%
% 9. Use the Model Explorer to move |numerator_param| from the base workspace
%    to the |airflow_calc| model workspace. 
%    For example, in the *Model Hierarchy* pane, select *Base Workspace*. Then, drag
%    |numerator_param| from the *Contents* pane to the *Model Workspace* node
%    in the *Model Hierarchy* pane. With this change, the code generator assumes that
%    |airflow_calc| owns |numerator_param|, and places the variable definition
%    in the code generated for |airflow_calc|.
%
% 10. In the |fuel_calc| model, on the *Modeling* tab, under *Design*, 
%     click *Property Inspector*.
%
% 11. For the Inport blocks |est_airflow| and |fb_correction|, use the 
%     Model Data Editor and the Property Inspector to set *Owner* to 
%     |airflow_calc|. With this configuration, the |fuel_calc| code does 
%     not define the variables.
%
% 12. For the Outport block, use the Model Data Editor and the Property
%     Inspector to set *Owner* to |fuel_calc|.
%
% Alternatively, to configure the data in the models, use these commands at
% the command prompt:
temp = Simulink.Signal;
temp.CoderInfo.StorageClass = 'Custom';
temp.CoderInfo.CustomStorageClass = 'ExportToFile';
temp.CoderInfo.CustomAttributes.Owner = 'airflow_calc';
 
set_param('airflow_calc/est_airflow','SignalName','est_airflow')
set_param('airflow_calc/est_airflow','SignalObject',copy(temp))
 
set_param('airflow_calc/fb_correction','SignalName','fb_correction')
set_param('airflow_calc/fb_correction','SignalObject',copy(temp))
 
mdlwks = get_param('airflow_calc','ModelWorkspace');
assignin(mdlwks,'numerator_param',copy(numerator_param));
 
portHandles = get_param('fuel_calc/est_airflow','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle,'Name','est_airflow')
set_param(outportHandle,'SignalObject',copy(temp))
 
portHandles = get_param('fuel_calc/fb_correction','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle,'Name','fb_correction')
set_param(outportHandle,'SignalObject',copy(temp))
 
temp.CoderInfo.CustomAttributes.Owner = 'fuel_calc';
 
set_param('fuel_calc/fuel_rate','SignalName','fuel_rate')
set_param('fuel_calc/fuel_rate','SignalObject',copy(temp))
 
clear temp portHandles outportHandle numerator_param
%%
% 13. In each of the three models, select *Configuration Parameters > Use owner
% from data object for data definition placement*. With this setting
% cleared (the default), the code generator ignores the values that you specify
% for *Owner*.
set_param('fuel_calc','EnableDataOwnership','on')
set_param('airflow_calc','EnableDataOwnership','on')
set_param('sldemo_fuelsys_dd_controller','EnableDataOwnership','on')
%%
% 14. Save the referenced models.
save_system('fuel_calc')
save_system('airflow_calc')
%% Generate Improved Code
% Generate code from the controller model, |sldemo_fuelsys_dd_controller|.
evalc('rtwbuild(''sldemo_fuelsys_dd_controller'')');
%%
% Now, the file |sldemo_fuelsys_dd_controller.c| does not define
% any of the global variables.
%
% In the code generation report, inspect the code generated for
% |airflow_calc|.
%
% The file |airflow_calc.c| now defines the global variables that belong to |airflow_calc|.
file = fullfile('slprj','ert','airflow_calc','airflow_calc.c');
rtwdemodbtype(file,'/* Definition for custom storage class: ExportToFile */',...
    'real32_T numerator_param[2] = { 0.01F, -0.01F } ;',1,1)
%%
% Inspect the code generated for |fuel_calc|. The file |fuel_calc.c| defines the global variable |fuel_rate|.
file = fullfile('slprj','ert','fuel_calc','fuel_calc.c');
rtwdemodbtype(file,'/* Definition for custom storage class: ExportToFile */',...
    'real32_T fuel_rate;',1,1)
%% Create Single Point of Specification for Signals That Pass Between Models
% The signal data elements |est_airflow| and |fb_correction| are represented 
% by Outport blocks in |airflow_calc| 
% and by Inport blocks in |fuel_calc|. If you want to change the configuration of one of
% these signals, you must make the change in both models. For example, if
% you want to change the storage class of |est_airflow| from |ExportToFile|
% to |Volatile|, you must change the storage class twice: once for the
% Outport block in |airflow_calc| and again for the Inport block in
% |fuel_calc|.
%
% To make maintenance of each signal easier, store the code generation
% settings in a |Simulink.Signal| object, which can exist in the base
% workspace or a data dictionary.
%
% 1. In the Model Data Editor for |airflow_calc|, on the *Inports/Outports* tab, 
%    find the row that corresponds to |est_airflow|. For that row, in the 
%    *Signal Name* column, click the cell.
%
% 2. In the cell, next to the |est_airflow| text, click the action button
%    (with three vertical dots). Select *Create and Resolve*.
%
% 3. In the *Create New Data* dialog box, set *Value* to |Simulink.Signal| and
%    click *Create*. A |Simulink.Signal| object named |est_airflow| appears in
%    the base workspace. In the Model Data Editor, for |est_airflow|, the
%    check box in the *Resolve* check box is selected, which means the Outport
%    block acquires code generation settings from the signal object in the
%    base workspace.
%
% 4. In the |est_airflow| property dialog box, set *Storage class* to
%    |ExportToFile|.
%
% 5. Set *Owner* to |airflow_calc|.
%
% 6. Use the Model Data Editor to create a similar signal object for |fb_correction|.
%
% In the Model Data Editor for |fuel_calc|, on the *Inports/Outports* tab,
% in the *Resolve* column, select the check boxes for |est_airflow| and
% |fb_correction|. Now, each Inport block acquires code generation settings
% from the corresponding signal object.
%
% Alternatively, to create the signal object and configure the blocks and
% lines in the model, at the command prompt, use these commands:
est_airflow = Simulink.Signal;
est_airflow.CoderInfo.StorageClass = 'Custom';
est_airflow.CoderInfo.CustomStorageClass = 'ExportToFile';
est_airflow.CoderInfo.CustomAttributes.Owner = 'airflow_calc';
 
fb_correction = Simulink.Signal;
fb_correction.CoderInfo.StorageClass = 'Custom';
fb_correction.CoderInfo.CustomStorageClass = 'ExportToFile';
fb_correction.CoderInfo.CustomAttributes.Owner = 'airflow_calc';
 
set_param('airflow_calc/est_airflow', 'StorageClass', 'Auto')
set_param('airflow_calc/est_airflow','MustResolveToSignalObject','on')
 
set_param('airflow_calc/fb_correction', 'StorageClass', 'Auto') 
set_param('airflow_calc/fb_correction','MustResolveToSignalObject','on')
 
portHandles = get_param('fuel_calc/est_airflow','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle, 'StorageClass', 'Auto')
set_param(outportHandle,'MustResolveToSignalObject','on')
 
portHandles = get_param('fuel_calc/fb_correction','portHandles');
outportHandle = portHandles.Outport;
set_param(outportHandle, 'StorageClass', 'Auto')
set_param(outportHandle,'MustResolveToSignalObject','on')
 
clear portHandles outportHandle 
%%
% 7. Save the models and generate code from |sldemo_fuelsys_dd_controller|. 
%    The code is the same as it was before you created the |Simulink.Signal| 
%    objects. Now, you can make changes to the signal objects instead of 
%    the corresponding blocks and lines in the models.
save_system('airflow_calc')
save_system('fuel_calc')
evalc('rtwbuild(''sldemo_fuelsys_dd_controller'')');

%% 
% Copyright 2020 The MathWorks, Inc.