Import Workspace Variables Using a Custom Data Reader
When your workspace data is in a format that built-in readers do not support, you can write a custom data reader to import the data into the Simulation Data Inspector. This example explains the parts of the class definition for a custom workspace reader and shows how to register the reader with the Simulation Data Inspector. Open the SimpleStructReader.m
file to view the complete class definition.
Create Workspace Data
First, create workspace data to import into the Simulation Data Inspector using the custom reader. Suppose you store each signal as a structure with fields for the data (d
), the time values (t
), and the signal name (n
).
time = 0:0.1:100; time = time'; lineData = 1/4*time; sineWave = sin((2*pi)/50*time); squareWave = square((2*pi)/30*time); mySineVar.d = sineWave; mySineVar.t = time; mySineVar.n = "Sine Wave"; myLineVar.d = lineData; myLineVar.t = time; myLineVar.n = "Line"; mySquareVar.d = squareWave; mySquareVar.t = time; mySquareVar.n = "Square Wave";
Write the Class Definition for a Custom Reader
Write a class definition that specifies how the custom reader extracts relevant data and metadata from the workspace variables. Save the class definition file in a location on the MATLAB® path.
The class definition starts by inheriting from the io.reader
class, followed by property and method definitions. The custom reader in this example defines the property ChannelIndex
. You can use the reader to import individual structures or an array of structures from the workspace. The ChannelIndex
property is used when importing an array of structures.
classdef SimpleStructReader < io.reader properties ChannelIndex end
Every custom reader must define the getName
, getTimeValues
, and getDataValues
methods. When you write a custom reader to import data from the workspace, you must also define the supportsVariable
method. The reader in this example also defines the getChildren
method to support importing an array of structures.
The supportsVariable
method checks which variables in the workspace are supported by the reader. In this example, the supportsVariable
returns true
when:
The structure contains the appropriate fields.
The
n
field of the structure contains a string or character array that represents the signal name.The
t
field of the structure is a column vector of double data.The
d
field contains numeric data.The
d
field is the same size as thet
field, meaning there is a sample value for each time step.
function supported = supportsVariable(~, val) % Support structure with fields t (time), d (data), and n (name) supported = ... isstruct(val) && ... isfield(val,'t') && ... isfield(val,'d') && ... isfield(val,'n'); if supported for idx = 1:numel(val) varName = val(idx).n; time = val(idx).t; varData = val(idx).d; % Name must be string or character array if ~ischar(varName) && ~isstring(varName) supported = false; % Time must be double column vector elseif ~isa(time,'double') || ~iscolumn(time) supported = false; % Data size must match time size else timeSz = size(time); dataSz = size(varData); if ~isnumeric(varData) || ~isequal(dataSz, timeSz) supported = false; end end end end end
The getChildren
method creates a SimpleStructReader
object for each structure in an array of structures. When the variable to import is not scalar, the getChildren
method assigns a value to the ChannelIndex
property added to the class for the custom reader. The VariableValue
property for each SimpleStructReader
object returned by the getChildren
method is the array of structures. Other methods use the ChannelIndex
property to extract the appropriate signal name, signal data, and time values from each object.
function childObj = getChildren(obj) childObj = {}; if ~isscalar(obj.VariableValue) && isempty(obj.ChannelIndex) numChannels = numel(obj.VariableValue); childObj = cell(numChannels,1); for idx = 1:numChannels childObj{idx} = SimpleStructReader; childObj{idx}.VariableName = sprintf('%s(%d)',obj.VariableName,idx); childObj{idx}.VariableValue = obj.VariableValue; childObj{idx}.ChannelIndex = idx; end end end
The getName
method assigns the name stored in the n
field of the structure to each imported signal. When the imported variable is scalar, the method gets the name from the VariableValue
property of the SimpleStructReader
object. When the imported data is an array of structures, the appropriate structure is extracted from the VariableValue
property using the ChannelIndex
property. The top-level node of the array is named Signal Array
.
function retName = getName(obj) if isscalar(obj.VariableValue) retName = char(obj.VariableValue.n); elseif ~isempty(obj.ChannelIndex) varVal = obj.VariableValue(obj.ChannelIndex); retName = char(varVal.n); else retName = 'Signal Array'; end end
The getTimeVals
and getDataVals
methods handle scalar and nonscalar structures similar to how the getName
method does. For a scalar structure, both methods extract the appropriate field from the VariableValue
property of the SimpleStructReader
object. For a nonscalar structure, both methods access the appropriate structure in the VariableValue
property using the ChannelIndex
property. Finally, for the top-level node of the array, time and data are both returned as empty.
function timeVals = getTimeValues(obj) if isscalar(obj.VariableValue) timeVals = obj.VariableValue.t; elseif ~isempty(obj.ChannelIndex) varVal = obj.VariableValue(obj.ChannelIndex); timeVals = varVal.t; else timeVals = []; end end function dataVals = getDataValues(obj) if isscalar(obj.VariableValue) dataVals = obj.VariableValue.d; elseif ~isempty(obj.ChannelIndex) varVal = obj.VariableValue(obj.ChannelIndex); dataVals = varVal.d; else dataVals = []; end end
Register a Custom Reader
After you write the class definition for the custom reader, you must register the reader before you can use it to import data into the Simulation Data Inspector. The Simulation Data Inspector does not store registered readers between MATLAB sessions, so you need to register a custom reader at the start of each new MATLAB session. To register the workspace data reader in this example, use the registerWorkspaceReader
method.
registerWorkspaceReader(SimpleStructReader);
To confirm that the reader is registered, use the io.reader.getRegisteredWorkspaceReaders
method.
io.reader.getRegisteredWorkspaceReaders
ans = "SimpleStructReader"
Import Workspace Data in a Custom Format
Once you register the custom workspace data reader, you can import workspace variables stored using the custom format into the Simulation Data Inspector using the UI or the Simulink.sdi.createRun
function.
To import data using the UI, open the Simulation Data Inspector. You can use the Simulink.sdi.view
function to open the Simulation Data Inspector from the MATLAB Command Window. Then, select Import .
The Import dialog box shows the data in the base workspace that the Simulation Data Inspector is able to import using built-in and registered custom readers. Because the custom reader is registered, the Line
, Sine Wave
, and Square Wave
signals are available for import, while the lineData
, sineWave
, and squareWave
variables are not. Select the data you want to import and select Import. To import all or none of the data, you can select or clear the check box next to NAME. The data imports into a run called Imported_Data
.
To import data from the workspace programmatically, use the Simulink.sdi.createRun
function.
Simulink.sdi.createRun('Custom Workspace Data Run','vars',myLineVar,mySineVar,mySquareVar);
The custom reader in this example can also import an array of structures. Importing an array of workspace variables rather than importing them individually groups the variables together when you import the data to an existing run. Create an array that contains the myLineVar
, mySineVar
, and mySquareVar
structures, and import the array using the Simulink.sdi.createRun
function.
myVarArray = [myLineVar; mySineVar; mySquareVar]; Simulink.sdi.createRun('Workspace Array Run','vars',myVarArray);
Inspect and Analyze Imported Data
After importing data, you can use the Simulation Data Inspector to inspect and analyze the imported data on its own or alongside related simulation data.