%% Configure Generated Code According to Interface Control Document Interactively
% This example shows how to configure code generation settings for a model according to
% specifications in an interface control document (ICD). Store necessary Simulink
% variables and objects, such as |Simulink.Parameter| objects, in a data
% dictionary.  
%
% An ICD describes the data interface between two software components. To
% exchange and share data, the components declare and define global
% variables that store signal and parameter values. The ICD names the
% variables and lists characteristics such as data type, physical units,
% and parameter values. When you create models of the components in
% Simulink, you can configure the generated code to conform to the
% interface specification.
%
% In this example, the ICD is a Microsoft(R) Excel(R) workbook.
%% Explore Interface Control Document
% Navigate to the folder
% |_matlabroot_/examples/ecoder|
% (<matlab:cd(fullfile(matlabroot,'examples','ecoder')) open>). Copy this
% file to a writable, working folder:
%
% * |ex_ICD_PCG_inter.xls|
%
% In Microsoft(R) Excel(R) or another compatible program, open the |ex_ICD_PCG_inter.xls| workbook
% and view the first worksheet, |Signals|. Each row of the worksheet
% describes a signal that crosses the interface boundary.
%
% Inspect the cell values in the worksheet. The |Owner| column indicates
% the name of the component that allocates memory for each signal. The
% |DataType| column indicates the signal data type in memory. For example,
% the worksheet uses the expression |Bus: EngSensors| to indicate a
% structure type named |EngSensors|.
%
% In the |Parameters| worksheet, the |Value| column indicates the value
% of each parameter. If the value of the parameter is nonscalar, the value
% is stored in its own separate worksheet, which has the same name as the
% parameter.
%
% In the |Numeric Types| worksheet, each row represents a named numeric
% data type. In this ICD, the data use fixed-point data types (Fixed-Point
% Designer). The |IsAlias| column indicates whether the C code uses
% the name of the data type (for example, |s16En3|) or uses the name of the
% primitive integer data type that corresponds to the word length (such as |short|). The
% |DataScope| column indicates whether the generated code exports or
% imports the definition of the type.
%
% In the |Structure Types| worksheet, each row represents either a
% structure type or a field of a structure type. For structure types, the
% value in the |DataType| column is |struct|. Subsequent rows that do not
% use |struct| represent fields of the preceding structure type. This ICD
% defines a structure type, |EngSensors|, with four fields: |throttle|,
% |speed|, |ego|, and |map|.
%
% In the |Enumerated Types| worksheet, similar to the |Structure Types|
% worksheet, each row represents either an enumerated type or an
% enumeration member. This ICD defines an enumerated type
% |sldemo_FuelModes|.
%% Write External Code
% Some data items in the ICD belong to |other_component|, which is a
% component that exists outside of MATLAB. Create the code files
% that define and declare this external data.
%
% Create the header file |ex_inter_types.h| in your current folder.
% This file defines the structure type |EngSensors| and numeric data types
% such as |u8En7|.
%
% <include>ex_inter_types.h</include>
% 
% Create the source file |ex_inter_sigs.c| in your current folder. This
% file defines the imported signal |sensors|.
%
% <include>ex_inter_sigs.c</include>
%
% Create the header file |ex_inter_sigs.h| in your current folder.
%
% <include>ex_inter_sigs.h</include>
%% Explore Example Model
% Run the script <matlab:run(fullfile(matlabroot,'examples','ecoder','main','prepare_sldemo_fuelsys_dd_inter'))
% |prepare_sldemo_fuelsys_dd_inter|>. For this example, the script prepares a system model,
% |sldemo_fuelsys_dd|.
%
%   run(fullfile(matlabroot,'examples','ecoder','main','prepare_sldemo_fuelsys_dd_inter'))
run(fullfile(matlabroot,'examples','ecoder','main','prepare_sldemo_fuelsys_dd_inter'))
%%
% Open the system model <matlab:sldemo_fuelsys_dd |sldemo_fuelsys_dd|>.
%
%   sldemo_fuelsys_dd
open_system('sldemo_fuelsys_dd')
%%
% This system model references a controller model. In this example, you
% generate code from the controller model.
%
% Open the controller model <matlab:sldemo_fuelsys_dd_controller
% |sldemo_fuelsys_dd_controller|>.
%
%   sldemo_fuelsys_dd_controller
open_system('sldemo_fuelsys_dd_controller')
%%
% Some of the signals in the controller model have names, for example, the input
% signal |sensors|. Some block parameters in the model refer to
% |Simulink.Parameter| objects in a data dictionary. For example, in the
% |airflow_calc| subsystem, the |Pumping Constant| block uses the parameter
% objects |PumpCon|, |SpeedVect|, and |PressVect|. These parameter objects
% set the values of the corresponding block parameters. You can apply code
% generation settings to the signals and parameter objects.
%
% The controller model is linked to a data dictionary,
% |sldemo_fuelsys_dd_controller.sldd|. In the lower-left corner of the
% model, click the model data badge, then click the *Data Dictionary* link.
% In the Model Explorer *Model Hierarchy* pane, select the *Design Data*
% node.
%
% The dictionary already stores:
%
% * The parameter objects
% * |Simulink.NumericType| objects such as |u8En7| 
% * A |Simulink.Bus| object, |EngSensors|
% * The definition of an enumerated data type, |sldemo_FuelModes|
%% Configure Model According to ICD
% Navigate to the root level of the controller model then, on the *Modeling* tab, 
% click *Model Data Editor*.
%
% In the Model Data Editor, activate the *Change scope* button. The Model
% Data Editor now shows information about data items in the subsystems.
%
% Click the 
% *Show/refresh additional information* button. The Model Data Editor now shows information
% about data objects (the |Simulink.Parameter| objects in the data
% dictionary) that the model uses.
%
% Select the *Inports/Outports* tab (which is selected by default).
%
% In the model, select the Inport block labeled |sensors|. The Model Data Editor
% highlights the corresponding row.
%
% In the ICD, select the *Signals* tab.
%
% Use the Model Data Editor to configure the signal according to the
% information in the ICD:
% 
% * Set the value in the *Data Type* column to |Bus: EngSensors|. In
% this case, the value is already set.
% * Set the *Change view* drop-down list to |Code| and, for |sensors|, set *Storage Class*
% to |ImportFromFile|. Use this storage class because the *Owner* column in
% the ICD implies that a different component, not
% |sldemo_fuelsys_dd_controller|, provides the C-code definition of the |sensors|
% variable. With this storage class, the generated code does not define the
% variable.
% * Set *Header File* to |ex_inter_sigs.h|. When you use a storage class
% that represents imported data, such as |ImportFromFile|, you cannot
% specify *Definition File* in the Model Data Editor. Instead, include the
% definition file (in this case, |ex_inter_sigs.c|) in the code generation
% and build process by using model configuration parameter *Source files*.
%
% In the model, select the Outport block labeled |fuel_rate|. 
%
% Use the Model Data Editor to configure |fuel_rate| according to the
% ICD. To access design properties such as minimum value (*Min*) and physical unit (*Unit*), 
% set *Change view* to |Design|. For code generation settings, because the 
% ICD specifies |sldemo_fuelsys_dd_controller| as the
% owner of the other signals, set *Storage Class* to |ExportToFile|. 
%
% Inspect the *Signals* tab.
%
% Set *Change view* to |Code| and configure code
% generation settings for the signal |fuel_mode|.
%
% You cannot use the Model Data Editor to configure design properties for |fuel_mode| (such as data type) because
% |fuel_mode| is an output of a Stateflow chart. In the model, navigate into the chart.
%
% On the *Modeling* tab, click *Model Explorer*.
%
% In the Model Explorer *Contents* pane (the middle pane), select the |fuel_mode| data item.
%
% In the Dialog pane (the right pane), configure |fuel_mode| according to
% the ICD. In this case, the signal data type is already set, so you can specify only the description.
%
% Navigate to the root level of the model.
%
% In the ICD and the Model Data Editor, select the *Parameters* tab. In the Model Data Editor, set
% *Change view* to |Design|.
%
% In the Model Data Editor, use the *Filter contents* box to search for the
% first parameter, |PressEst|. The Model Data Editor shows two rows: One
% row
% that corresponds to the parameter object |PressEst| and one row that
% corresponds to the block parameter that uses |PressEst|.
%
% Use the Model Data Editor to configure |PressEst| according to the ICD.
% The parameter value (the *Value* column) is already set. Because
% the ICD specifies |sldemo_fuelsys_dd_controller| as the owner of
% |PressEst|, set *Storage Class* to |ExportToFile|.
%
% Use the Model Data Editor to configure the other parameters. Optionally,
% to apply a change to multiple parameters at once, select multiple rows in
% the data table.
%
% In the lower-left corner of the model, click the model data badge, then
% click the *Data Dictionary* link to open the data dictionary in the Model
% Explorer.
%
% In the *Model Hierarchy* pane, select the *Design Data* node.
%
% In the *Contents* pane (the middle pane), select the
% |Simulink.NumericType| object |u8En7|. This object represents one of the
% primitive |typedef| statements in |ex_inter_types.h|.
%
% In the ICD, select the *Numeric Types* tab.
%
% Use the Model Explorer Dialog pane (the right pane) to configure the
% object according to the ICD.
%
% Use the Model Explorer to configure the other |Simulink.NumericType|
% objects. You can use the *Contents* pane to perform batch operations.
%
% In the *Contents* pane, click the |Simulink.Bus| object |EngSensors|. 
% This object represents the structure type that |ex_inter_types.h|
% defines.
%
% In the Dialog pane (the right pane), click *Launch Bus Editor*.
%
% In the ICD, select the *Structure Types* tab.
%
% Use the Bus Editor to configure the bus object and the signal elements
% in the bus (such as |throttle|) according to the ICD.
%
% In the ICD, select the *Enumerated Types* tab.
%
% In the Model Explorer *Contents* pane, click the enumerated type
% definition |sldemo_FuelModes|.
%
% Use the Dialog pane to configure the type according to the ICD. Set
% *Storage Type* to |Native Integer| and *Data Scope* to |Exported|.
%% Generate and Inspect Code
% Configure the controller model to compile the generated code into an
% executable program by clearing the model configuration parameter *Generate code
% only*.
set_param('sldemo_fuelsys_dd_controller','GenCodeOnly','off')
%%
% Generate code from the controller model.
 
load_system('sldemo_fuelsys_dd_controller')
set_param('sldemo_fuelsys_dd_controller','PortableWordSizes','on')
 
temp = Simulink.Signal;
temp.CoderInfo.StorageClass = 'Custom';
temp.CoderInfo.CustomStorageClass = 'ImportFromFile';
temp.CoderInfo.CustomAttributes.HeaderFile = 'ex_inter_sigs.h';
portHandles = get_param('sldemo_fuelsys_dd_controller/sensors','portHandles');
portHandle = portHandles.Outport;
set_param(portHandle,'SignalObject',copy(temp));
 
temp.CoderInfo.CustomStorageClass = 'ExportToFile';
temp.CoderInfo.CustomAttributes.HeaderFile = 'global_data.h';
temp.CoderInfo.CustomAttributes.DefinitionFile = 'signals.c';
portHandles = get_param('sldemo_fuelsys_dd_controller/fuel_calc','portHandles');
portHandle = portHandles.Outport;
set_param(portHandle,'MustResolveToSignalObject','off');
set_param('sldemo_fuelsys_dd_controller/fuel_rate','OutDataTypeStr','s16En7');
set_param('sldemo_fuelsys_dd_controller/fuel_rate','Unit','g/sec');
set_param('sldemo_fuelsys_dd_controller/fuel_rate','SignalName','fuel_rate');
set_param('sldemo_fuelsys_dd_controller/fuel_rate','SignalObject',copy(temp));
 
portHandles = get_param('sldemo_fuelsys_dd_controller/control_logic','portHandles');
portHandle = portHandles.Outport(3);
set_param(portHandle,'SignalObject',copy(temp));
rt = sfroot;
m = rt.find('-isa','Simulink.BlockDiagram','-and','Name','sldemo_fuelsys_dd_controller');
ch = m.find('-isa','Stateflow.Chart');
fuel_mode_data = find(ch,'-isa','Stateflow.Data','-and','Name','fuel_mode');
fuel_mode_data.Description = 'Fueling mode of engine. Enrich air/fuel mixture on sensor failure.';
 
dictObj = Simulink.data.dictionary.open('sldemo_fuelsys_dd.sldd');
dDataSectObj = getSection(dictObj,'Design Data');
 
paramNames = {'PressEst','PumpCon','SpeedEst','ThrotEst','RampRateKiZ'};
for i = 1:length(paramNames)
    temp = getValue(getEntry(dDataSectObj,paramNames{i}));
    if isa(temp,'Simulink.Parameter')
        temp.CoderInfo.StorageClass = 'Custom';
        temp.CoderInfo.CustomStorageClass = 'ExportToFile';
        temp.CoderInfo.CustomAttributes.HeaderFile = 'global_data.h';
        temp.CoderInfo.CustomAttributes.DefinitionFile = 'params.c';
        switch paramNames{i}
            case 'PressEst'
                temp.DataType = 'u8En7';
                temp.Unit = 'bar';
            case 'PumpCon'
                temp.DataType = 's16En15';
            case 'SpeedEst'
                temp.DataType = 's16En3';
                temp.Unit = 'rad/sec';
            case 'ThrotEst'
                temp.DataType = 's16En7';
                temp.Unit = 'deg';
            case 'RampRateKiZ'
                temp.DataType = 's16En15';
                temp.Unit = 'g/sec^2';
        end
        setValue(getEntry(dDataSectObj,paramNames{i}),copy(temp));
    end
end
 
temp = getValue(getEntry(dDataSectObj,'EngSensors'));
temp.DataScope = 'Imported';
temp.HeaderFile = 'ex_inter_types.h';
temp.Description = 'Structure type for instrument measurements.';
temp.Elements(1).DataType = 's16En3';
temp.Elements(1).Min = 5;
temp.Elements(1).Max = 25;
temp.Elements(1).Unit = 'deg';
temp.Elements(1).Description = 'Throttle angle.';
temp.Elements(2).DataType = 's16En3';
temp.Elements(2).Min = 300;
temp.Elements(2).Max = 700;
temp.Elements(2).Unit = 'rad/sec';
temp.Elements(2).Description = 'Engine speed.';
temp.Elements(3).DataType = 's16En7';
temp.Elements(3).Min = 0.3;
temp.Elements(3).Max = 1;
temp.Elements(3).Unit = 'V';
temp.Elements(3).Description = 'EGO sensors.';
temp.Elements(4).DataType = 'u8En7';
temp.Elements(4).Min = 0.2;
temp.Elements(4).Max = 1.2;
temp.Elements(4).Unit = 'bar';
temp.Elements(4).Description = 'Manifold pressure.';
 
setValue(getEntry(dDataSectObj,'EngSensors'),copy(temp));
 
typeNames = {'u8En7','s16En3','s16En7','s16En15'};
for i = 1:length(typeNames)
    temp = getValue(getEntry(dDataSectObj,typeNames{i}));
    temp.DataTypeMode = 'Fixed-point: binary point scaling';
    temp.DataScope = 'Imported';
    temp.HeaderFile = 'ex_inter_types.h';
    temp.IsAlias = 1;
    switch typeNames{i}
        case 'u8En7'
            temp.Signedness = 'Unsigned';
            temp.WordLength = 8;
            temp.FractionLength = 7;
        case 's16En3'
            temp.Signedness = 'Signed';
            temp.WordLength = 16;
            temp.FractionLength = 3;
        case 's16En7'
            temp.Signedness = 'Signed';
            temp.WordLength = 16;
            temp.FractionLength = 7;
        case 's16En15'
            temp.Signedness = 'Signed';
            temp.WordLength = 16;
            temp.FractionLength = 15;
    end
    setValue(getEntry(dDataSectObj,typeNames{i}),copy(temp));
end
 
set_param('sldemo_fuelsys_dd_controller','CustomSource','ex_inter_sigs.c')
 
temp = getValue(getEntry(dDataSectObj,'sldemo_FuelModes'));
temp.DataScope = 'Exported';
ctrl_dictObj = Simulink.data.dictionary.open('sldemo_fuelsys_dd_controller.sldd');
ctrl_dDataSectObj = getSection(ctrl_dictObj,'Design Data');
setValue(getEntry(ctrl_dDataSectObj,'sldemo_FuelModes'),temp);
 
rtwbuild('sldemo_fuelsys_dd_controller')
%%
% The generated header file |sldemo_FuelModes.h| defines the enumeration
% |sldemo_FuelModes|.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_FuelModes.h');
rtwdemodbtype(file,'typedef enum {','} sldemo_FuelModes;',1,1)
%%
% The file |sldemo_fuelsys_dd_controller_types.h| includes (|#include|) the
% external header file |ex_inter_types.h|, which defines data types such as
% |u8En7| and the structure type |EngSensors|.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_fuelsys_dd_controller_types.h');
rtwdemodbtype(file,'#include "ex_inter_types.h"','#include "ex_inter_types.h"',1,1)
%%
% The file |sldemo_fuelsys_dd_controller_private.h| includes the
% header file |ex_inter_sigs.h|. This external header file contains the |extern|
% declaration of the signal |sensors|, which a different software component
% owns.
%
% The data header file |global_data.h| declares the exported parameters and
% signals that the ICD specifies. To share this data, other components can
% include this header file.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw','global_data.h');
rtwdemodbtype(file,'/* Exported data declaration */',...
    'fuel_rate',1,1)
%%
% The data definitions (memory allocation) appear in the source files that
% the ICD specifies: |params.c| and |signals.c|. For example, |params.c| defines and
% initializes the parameter |RampRateKiZ|.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw','params.c');
rtwdemodbtype(file,'s16En15 RampRateKiZ[25]',...
    '9830 } ;',1,1)
%%
% The algorithm is in the model |step| function in the file
% |sldemo_fuelsys_dd_controller.c|. The algorithm uses the global data that
% the ICD identifies. For example, the algorithm uses the value of the
% signal |fuel_mode| in a |switch| block to control the flow of execution.
file = fullfile('sldemo_fuelsys_dd_controller_ert_rtw',...
    'sldemo_fuelsys_dd_controller.c');
rtwdemodbtype(file,'/* End of MultiPortSwitch: ''<S9>/Multiport Switch'' */',...
    '/* Outport: ''<Root>/fuel_rate'' incorporates:',0,0)

bdclose('sldemo_fuelsys_dd')
bdclose('sldemo_fuelsys_dd_controller')
Simulink.data.dictionary.closeAll('sldemo_fuelsys_dd.sldd','-discard')

%% 
% Copyright 2019 The MathWorks, Inc.