classdef (StrictDefaults)LogicAnalyzer < matlabshared.scopes.WebWindowSystemScope
    %LogicAnalyzer Display transitions of signals over time
    %   H = dsp.LogicAnalyzer returns a System object, H, that can display
    %   transitions of real and complex valued floating, fixed-point and
    %   enumerated signals.
    %
    %   H = dsp.LogicAnalyzer('Name', Value, ...) returns a Logic Analyzer System
    %   object, H, with each specified property name set to the specified
    %   value. You can specify name-value pair arguments in any order as
    %   (Name 1, Value 1, ..., Name N, Value N).
    %
    %   H = dsp.LogicAnalyzer(NUMINPUTPORTS, SAMPLETIME, 'PropertyName',
    %   PropertyValue, ...) returns a Logic Analyzer System object, H, with the
    %   NumInputPorts property set to NUMINPUTPORTS, the SampleTime property set to
    %   SampleTime, and other specified properties set to the specified values.
    %   NUMINPUTPORTS and SAMPLETIME are value-only arguments. To specify a
    %   value-only argument, you must also specify all preceding value-only
    %   arguments. You can specify name-value pair arguments in any order.
    %
    %   Step method syntax:
    %
    %   step(H, X) displays the signal, X, in the Logic Analyzer figure.
    %
    %   step(H, X1, X2, ..., XN) displays the signals X1, X2, ..., XN in the
    %   Logic Analyzer figure when the NumInputPorts property is set to N.
    %   X1, X2,... XN can have different data types.
    %
    %   LogicAnalyzer methods:
    %
    %   step                  - Display signal in the Logic Analyzer figure (see above)
    %   release               - Allow property value and input characteristics changes, and
    %                           release Logic Analyzer resources
    %   clone                 - Create Logic Analyzer object with the same property values
    %   isLocked              - Display locked status (logical)
    %   show                  - Turn on visibility of Logic Analyzer figure
    %   hide                  - Turn off visibility of Logic Analyzer figure
    %   addWave               - Add a wave corresponding to the specified input
    %   addDivider            - Add a divider to the display
    %   modifyDisplayChannel  - Modify the properties of the specified display channel
    %   moveDisplayChannel    - Move the position of the specified display channel
    %   deleteDisplayChannel  - Delete the specified display channel
    %   addCursor             - Add a cursor to the display
    %   modifyCursor          - Modify the properties of the specified cursor
    %   deleteCursor          - Delete the specified cursor
    %   getDisplayChannelTags - Return all the Display Channel tags
    %   getCursorTags         - Return all the Cursor tags
    %   getDisplayChannelInfo - Return settings for the Display Channel
    %   getCursorInfo         - Return settings for the Cursor
    %   isVisible             - Return visibility of Logic Analyzer figure (logical)
    %
    %   LogicAnalyzer properties:
    %
    %   Name                   - Caption to display on the Logic Analyzer window
    %   NumInputPorts          - Number of inputs
    %   SampleTime             - Sample time of inputs
    %   Position               - Scope window position in pixels
    %   TimeSpan               - Time span for the display in the figure
    %   TimeDisplayOffset      - Time display offset
    %   DisplayChannelRadix    - Radix for channels in the display
    %   DisplayChannelHeight   - Vertical space (height) of channels in the display
    %   DisplayChannelColor    - Color for channels in the display
    %   DisplayChannelFormat   - Format for channels in the display
    %   DisplayChannelFontSize - Font size for values in the display
    %   DisplayChannelSpacing  - Spacing between channels in the display
    %   BackgroundColor        - Background color for the display
    %
    %   % EXAMPLE #1: View three signals on a Logic Analyzer.
    %      H1 = dsp.LogicAnalyzer('NumInputPorts', 3, 'DisplayChannelFormat', 'Digital');
    %      for cnt = 1:20
    %         step(H1, cnt, 10*cnt, 20*cnt);
    %      end
    %      % Run the release method to be able to change the properties of the Logic Analyzer.
    %      release(H1);
    %
    %   % EXAMPLE #2: View two signals on a Logic Analyzer. View the second
    %   %   input in two different formats (hexadecimal and binary). Add
    %   %   appropriate labels for each channel in the display.
    %   %   This example requires Fixed-Point Designer.
    %      H2 = dsp.LogicAnalyzer('NumInputPorts', 2); % 2 inputs
    %      % Display from time 0 to 12 in the figure
    %      H2.TimeSpan = 12;
    %      tags = getDisplayChannelTags(H2);
    %      % First display channel is the first input
    %      modifyDisplayChannel(H2, tags{1}, 'Name', 'Index');
    %      % Second display channel is the second input in hexadecimal
    %      % format
    %      modifyDisplayChannel(H2, tags{2}, 'Name', 'FiHex', 'Radix', 'Hexadecimal');
    %      % Third display channel is the second input in binary format
    %      addWave(H2, 'InputChannel', 2, 'Name', 'FiBin', 'Radix', 'Binary');
    %      for cnt = 1:20
    %         % fival: fixed-point, unsigned, 4 bits word length
    %         fival = fi(mod(cnt-1, 16), 0, 4, 0);
    %         step(H2, cnt, fival);
    %      end
    %      % Run the release method to be able to change the properties of the Logic Analyzer.
    %      release(H2);
    %
    %   % EXAMPLE #3: View vector and complex signals on a Logic Analyzer.
    %      H3 = dsp.LogicAnalyzer('NumInputPorts', 2, 'DisplayChannelFormat', 'Digital');
    %      tags = getDisplayChannelTags(H3);
    %      modifyDisplayChannel(H3, tags{1}, 'Name', 'Vector Signal');
    %      modifyDisplayChannel(H3, tags{2}, 'Name', 'Complex Signal');
    %      for cnt = 1:20
    %         val = randi(9); % random single digit value
    %         step(H3, [val cnt cnt-val], complex(val, cnt-val));
    %      end
    %      % Run the release method to be able to change the properties of the Logic Analyzer.
    %      release(H3);
    %
    %   See also timescope, spectrumAnalyzer, dsp.ArrayPlot.
    
    %   Copyright 2012-2023 The MathWorks, Inc.
    
    % %#codegen
    
    properties
        %Name Caption to display on the Logic Analyzer window
        %   Specify the title to display on the Logic Analyzer window as a
        %   string. The default value of this property is 'Logic Analyzer'.
        %   This property is tunable.
        Name = 'Logic Analyzer';
    end
    
    properties (Nontunable)
        %SampleTime Sample time of inputs
        %   Specify the sampling time of the input ports in seconds as a
        %   finite numeric scalar. The same sample time is used for all
        %   inputs. The default value of this property is 1.
        SampleTime = 1;
    end
    
    properties (Nontunable, Dependent)
        %NumInputPorts Number of input ports
        %   Specify the number of input ports to the Logic Analyzer System
        %   object as a positive integer. You must invoke the step method
        %   with the same number of inputs as the value of this property.
        %   The default value of this property is 1.
        NumInputPorts;
    end
    
    properties (Nontunable, Hidden)
        MaxNumTimeSteps = 50000;
    end
    
    properties (Nontunable, Hidden)
        UseFrameBasedDisplay = false;
    end
    
    properties (Dependent)
        %TimeSpan Time span for the display in the figure
        %   Specify the display time span as a positive numeric scalar in units
        %   of seconds. This property is tunable.
        TimeSpan;
        
        %TimeDisplayOffset Time display offset for the first sample
        %   Specify the offset to apply to the time axis (x-axis) in units of
        %   seconds. This property can either be set to a numeric scalar. The
        %   default value of this property is 0. This property is tunable.
        TimeDisplayOffset;
    end
    
    properties
        
        %DisplayChannelRadix Radix for channels in the display
        %   Specify DisplayChannelRadix to be one of 'Hexadecimal' | 'Octal' | 'Binary' |
        %   'Signed Decimal' | 'Unsigned decimal'. This property does not
        %   affect double, single and logical inputs. The default value is
        %   'Hexadecimal'. This property is tunable.
        DisplayChannelRadix;
        
        %DisplayChannelHeight Vertical space (height) of channels in the display
        %   Specify the height of channels in the display as a scalar
        %   positive integer in units of pixels.
        %   This property is tunable.
        DisplayChannelHeight;
        
        %DisplayChannelColor Color for channels in the display
        %   Specify DisplayChannelColor to be one of 'Black' | 'Blue' |
        %   'Green' | 'Cyan' | 'Red' | 'Magenta' | 'Yellow' | 'White'. The
        %   default value of this property is 'Cyan'. This property is
        %   tunable.
        DisplayChannelColor;
        
        % DisplayChannelFormat Format for channels in the display
        %   Specify DisplayChannelFormat to be one of 'Automatic' |
        %   'Digital' | 'Analog'.  The default value of this property is
        %   'Automatic'. 'Automatic' will use 'Analog' for floating-point
        %   signals and 'Digital' for fixed-point signals.  This property
        %   is tunable.
        DisplayChannelFormat;
        
        % DisplayChannelFontSize Font size for values in the display
        %   Specify the default font size in points, for values being shown
        %   in the channels in the display.This property is tunable.
        DisplayChannelFontSize;
        
        % DisplayChannelSpacing Spacing between the channels in the display
        %   Specify the spacing between channels in the display as a scalar
        %   positive integer in units of pixels.
        %   This property is tunable.
        DisplayChannelSpacing;
        
        % BackgroundColor Background color for the display
        %   Specify BackgroundColor to be one of 'Black'| 'White'.
        %   The default value of this property is 'Black'. This property is
        %   tunable.
        BackgroundColor = 'Black';
    end
    
    properties (Hidden, Dependent)
        %DisplayChannels List of channels in the display
        %   Specify an array of LogicAnalyzerWave and LogicAnalyzerDivider
        %   objects to be displayed in the Logic Analyzer window.
        %   This property is tunable.
        DisplayChannels;
        
        %Cursors List of cursors
        %   Specify an array of cursor objects to be displayed in the Logic
        %   Analyzer window. For help on cursor objects, run
        %   'help dsp.LogicAnalyzerCursor'. This property is tunable.
        Cursors;
    end
    
    properties(Hidden)
        ReduceUpdates (1, 1) logical = true;
    end
    
    properties(Constant, Hidden)
        % Specify DisplayChannelRadix to be one of 'Hexadecimal' | 'Octal'
        % | 'Binary' | 'Signed decimal' | 'Unsigned decimal'.
        DisplayChannelRadixSet = matlab.system.StringSet(getLogicAnalyzerDisplayRadixSet);
        
        % Specify BackgroundColor to be one of 'Black'| 'White'.
        BackgroundColorSet = matlab.system.StringSet({'Black', 'White'});
        
        % Specify DisplayChannelFormat to be one of 'Digital'| 'Analog'
        DisplayChannelFormatSet = matlab.system.StringSet(getLogicAnalyzerDisplayFormatSet);
    end
    
    properties (SetAccess = private, Hidden)
        LAModelController;
        AsyncQueues;
        TimeCount = 0;
        LastTime = 0;
        LastWriteTime;
    end
    
    methods
        function obj = LogicAnalyzer(varargin)
            % license checkout
            obj@matlabshared.scopes.WebWindowSystemScope();
            licenseCheckout;
            
            % constructor
            
            % set up function to update visual when plotter changes
            
            obj.Position = uiscopes.getDefaultPosition([800 600]);
            
            % set default values here
            obj.TimeSpan = 10;
            obj.TimeDisplayOffset = 0;
            obj.Name = 'Logic Analyzer';
            
            % Add string support.
            if nargin > 0
                firstProp = find(cellfun(@ischar, varargin), 1, 'first');
                varargin(firstProp:2:end) = cellstr(varargin(firstProp:2:end));
            end
            
            setProperties(obj, nargin, varargin{:}, 'NumInputPorts', 'SampleTime');
            
            obj.setDebugLevel(3);
            
            modelHandle = -1;
            obj.LAModelController = Simulink.scopes.LAScope.GetNonEmptyInstanceForModel(modelHandle,obj.MessageHandler.ClientId);
            obj.LastWriteTime = tic;
        end
        
        function set.Name(obj, value)
            import matlab.internal.capability.Capability;
            % Add String Support
            if nargin > 1
                value = convertStringsToChars(value);
            end
            % Display Title of Logic Analyzer window
            if ~ischar(value) && ~isstring(value)
                localError('Name');
            end
            obj.Name = value;
            if  ~Capability.isSupported(Capability.LocalClient)
                message.publish('/web/logicAnalyzer', struct('peerNodeId', obj.MessageHandler.ClientId, 'name', value, 'eventType', 'windowPropertyChanged'));
            else
                setScopeName(obj,value);
            end
        end
        
        function set.NumInputPorts(obj, value)
            % Number of inputs
            validateattributes(value, ...
                {'numeric'}, {'integer', 'scalar', '>=', 1, '<=', 10000}, '', 'NumInputPorts'); %#ok<*EMCA>
            obj.MessageHandler.NumInputs = value;
        end
        
        function inputs = get.NumInputPorts(obj)
            inputs = obj.MessageHandler.NumInputs;
        end
        
        function set.ReduceUpdates(obj, v)
            obj.ReduceUpdates = v;
            warning(message('Spcuilib:logicanalyzer:ReduceUpdatesObsolete'));
        end
        
        function set.SampleTime(obj, value)
            % Sample time of inputs
            validateattributes(value, ...
                {'numeric'},{'positive','finite'}, '', 'SampleTime');
            obj.SampleTime = value;
        end
        
        function set.TimeSpan(this, value)
            % Amount of time that is displayed in current view
            % TimeSpan must be a real positive scalar
            validateattributes(value, ...
                {'numeric'}, {'real','finite','positive','scalar'},'', 'TimeSpan');
            this.MessageHandler.TimeSpan = value;
            this.MessageHandler.ClientSettingsStale = true;
            this.callMethod('setTimeSpan', value);
        end
        
        function value = get.TimeSpan(this)
            handler = this.MessageHandler;
            if handler.ClientSettingsStale
                value = handler.TimeSpan;
            else
                value = handler.ClientSettings.timeRange;
            end
        end
        
        function set.TimeDisplayOffset(this, value)
            % Time OffsetAmount for first data displayed in current view
            validateattributes(value, ...
                {'numeric'}, {'real','finite','nonnegative','scalar'},'', ...
                'TimeDisplayOffset');
            this.MessageHandler.TimeDisplayOffset = value;
            this.MessageHandler.ClientSettingsStale = true;
            this.callMethod('setStartTime', value);
        end
        
        function value = get.TimeDisplayOffset(this)
            handler = this.MessageHandler;
            if handler.ClientSettingsStale
                value = handler.TimeDisplayOffset;
            else
                value = handler.ClientSettings.startTime;
            end
        end
        
        function set.DisplayChannels(obj, value)
            % List of all waves and dividers being displayed
            % Must be a cell array of dsp.LogicAnalyzerWave or
            % dsp.LogicAnalyzerDivider objects
            dsp.LogicAnalyzer.validateCellObject(value, ...
                'dsp.AbstractLogicAnalyzerDisplay', 'invalidDisplayChannels');
            obj.MessageHandler.DisplayChannels = value;
        end
        
        function value = get.DisplayChannels(obj)
            value = obj.MessageHandler.DisplayChannels;
        end
        function set.Cursors(obj, value)
            % List of cursors in the Logic Analyzer
            % must be a cell array of dsp.LogicAnalyzerCursor objects
            dsp.LogicAnalyzer.validateCellObject(value, 'dsp.LogicAnalyzerCursor', ...
                'invalidCursors');
            
            obj.MessageHandler.Cursors = value;
        end
        
        function value = get.Cursors(obj)
            % List of cursors in the Logic Analyzer
            % get cursor information from plotter and send out as a cell array
            % of dsp.LogicAnalyzerCursors
            value = obj.MessageHandler.Cursors;
        end
        
        function set.DisplayChannelFontSize(obj, value)
            % Font size for text ebing displayed
            % non-zero positive scalar integer value
            validateattributes(value, ...
                {'numeric'}, {'real','finite','positive','scalar','integer'},'', ...
                'DisplayChannelFontSize');
            obj.MessageHandler.setGlobalSetting('fontSize', value);
        end
        
        function value = get.DisplayChannelFontSize(obj)
            value = obj.MessageHandler.getGlobalSetting('fontSize', 12);
        end
        
        function set.DisplayChannelHeight(obj, value)
            % Height for the display
            % non-zero positive scalar integer value
            validateattributes(value, ...
                {'numeric'}, {'real','finite','positive','scalar','integer'},'', ...
                'DisplayChannelHeight');
            
            if value < 8
                error(message('Spcuilib:logicanalyzer:invalidDisplayChannelHeight'));
            end
            
            obj.MessageHandler.setGlobalSetting('height', value);
        end
        
        function value = get.DisplayChannelHeight(obj)
            value = obj.MessageHandler.getGlobalSetting('height', 12);
        end
        
        function set.DisplayChannelSpacing(obj, value)
            % Spacing between two successive displays
            % non-zero positive scalar integer value
            validateattributes(value, ...
                {'numeric'}, {'real','finite','positive','scalar','integer'},'', ...
                'DisplayChannelSpacing');
            obj.MessageHandler.setGlobalSetting('spacing', value);
        end
        
        function value = get.DisplayChannelSpacing(obj)
            value = obj.MessageHandler.getGlobalSetting('spacing', 4);
        end
        
        function set.DisplayChannelRadix(obj, value)
            % Default radix for all Displays
            obj.MessageHandler.setGlobalSetting('radix', strrep(char(lower(value)), ' ', ''));
        end
        
        function value = get.DisplayChannelRadix(obj)
            value = obj.MessageHandler.getGlobalSetting('radix', 'hexadecimal');
            switch value
                case 'signeddecimal'
                    value = 'Signed decimal';
                case 'unsigneddecimal'
                    value = 'Unsigned decimal';
                otherwise
                    value(1) = upper(value(1));
            end
        end
        
        function set.DisplayChannelColor(obj, value)
            % Default color for all Displays
            if ischar(value) || isstring(value)
                value = uiservices.stringColorToHexColor(value);
            else
                value = uiservices.rgbToHexColor(value);
            end
            obj.MessageHandler.setGlobalSetting('color', value);
        end
        
        function value = get.DisplayChannelColor(obj)
            value = obj.MessageHandler.getGlobalSetting('color', '#0fffff');
            value = uiservices.hexColorToRGB(value);
        end
        
        function set.DisplayChannelFormat(obj, value)
            if strcmpi(value, 'automatic')
                value = 'auto';
            end
            obj.MessageHandler.setGlobalSetting('format', lower(char(value)));
        end
        
        function value = get.DisplayChannelFormat(obj)
            value = obj.MessageHandler.getGlobalSetting('format', 'auto');
            if strcmp(value, 'auto')
                value = 'automatic';
            end
            value(1) = upper(value(1));
        end
        
        function set.BackgroundColor(obj, value)
            % Default background color for all Displays
            if strcmpi(value, 'black')
                value = '#000000';
            else
                value = '#ffffff';
            end
            obj.MessageHandler.setGlobalSetting('backgroundColor', value);
        end
        
        function value = get.BackgroundColor(obj)
            % Default background color for all Displays
            value = obj.MessageHandler.getGlobalSetting('backgroundColor', '#000000');
            
            if strcmp(value, '#000000')
                value = 'Black';
            else
                value = 'White';
            end
        end
        
        function varargout = addWave(this,varargin)
            %addWave Add a wave corresponding to the specified input
            %   waveTag = addWave(H, 'Name', Value, ...)
            %   adds in a wave corresponding to the specified input
            %   channel. A tag value is returned back, which can be used
            %   to modify and delete the wave (see modifyDisplayChannel,
            %   moveDisplayChannel, deleteDisplayChannel).
            %   The following name-value pairs may be passed in to this
            %   method:
            %   InputChannel Input channel that corresponds to this wave
            %      The property specifies the input channel whose data is
            %      used for this wave. The default value for this property
            %      is 1 (connect the first input to this wave).
            %   DisplayChannel Channel on the display that shows this wave
            %      The property specifies the display channel. By default,
            %      the wave is added to the end of the display.
            %   Name Name/label for the wave
            %      The default value for this property is empty.
            %   Color Color of the wave
            %      Specify Color to be one of 'Default' | 'Black' | 'Blue' |
            %      'Green' | 'Cyan' | 'Red' | 'Magenta' | 'Yellow' |
            %      'White'. When 'Default' is specified, the value of the
            %      DisplayChannelColor property in the Logic Analyzer is
            %      used. The default value for this property is 'Default'.
            %   Height Vertical space (height) of the wave
            %      Specify the height of the wave in the display as a scalar
            %      integer in units of pixels. When the value specified
            %      is 0, the height specified in the DisplayChannelHeight
            %      property in the Logic Analyzer is used.
            %      The default value for this property is 0.
            %   Radix Radix for wave
            %      Specify Radix to be one of 'Default' | 'Hexadecimal' |
            %      'Octal' | 'Binary' | 'Signed decimal' | 'Unsigned decimal'.
            %      This property does not apply to double, single and
            %      logical inputs. When 'Default' is specified, the radix
            %      specified by the DisplayChannelRadix property in the
            %      Logic Analyzer is used. The default value for this
            %      property is 'Default'.
            %   FontSize Font size for values in the wave
            %      Specify the font size to be a scalar non-negative integer
            %      in points. When the value 0 is specified, the font size
            %      specified by the DisplayChannelFontSize property in the
            %      Logic Analyzer is used. The default value for this property
            %      is 0.
            %   Format Display format for the wave
            %      Specify Format to be one of 'Default' | 'Automatic' | 'Digital' |
            %      'Analog'.  When 'Default' is specified, the display format
            %      specified by the DisplayChannelFormat property in the Logic
            %      Analyzer is used. The default value of this property is 'Default'.
            %
            %   See also dsp.LogicAnalyzer/addDivider, dsp.LogicAnalyzer/modifyDisplayChannel,
            %            dsp.LogicAnalyzer/moveDisplayChannel,
            %            dsp.LogicAnalyzer/deleteDisplayChannel.
            
            validateParamNames(varargin(1:2:end), {'InputChannel', 'DisplayChannel', 'Name', 'Color', 'Height', 'Radix', 'FontSize', 'Format'}, 'addWave');
            
            try
                newWaveTag = this.MessageHandler.addWave(varargin{:});
            catch me
                throwAsCaller(me);
            end
            
            % if nargout = 1, send out the tag of the wave added in
            if nargout > 0
                varargout{1} = newWaveTag;
            end
        end % addWave
        
        function varargout = addDivider(this,varargin)
            %addDivider Add a divider to the display
            %   dividerTag = addDivider(H, 'Name', Value, ...)
            %   adds in a divider to the display. A tag value is returned
            %   which can be used to modify and delete the divider
            %   (see modifyDisplayChannel, deleteDisplayChannel).
            %   The following name-value pairs may be passed in to this
            %   method:
            %
            %   DisplayChannel Channel on the display that shows this divider
            %      The property specifies the display channel. By default,
            %      the divider is added to the end of the display.
            %   Name Name/label for the divider
            %      The default value for this property is empty.
            %   Height Vertical space (height) of the divider
            %      Specify the height of the divider in the display as a scalar
            %      integer in units of pixels. When the value specified
            %      is 0, the height specified in the DisplayChannelHeight
            %      property in the Logic Analyzer is used.
            %      The default value for this property is 0.
            %
            %   See also dsp.LogicAnalyzer/addWave, dsp.LogicAnalyzer/modifyDisplayChannel,
            %            dsp.LogicAnalyzer/moveDisplayChannel,
            %            dsp.LogicAnalyzer/deleteDisplayChannel.
            
            validateParamNames(varargin(1:2:end), {'DisplayChannel', 'Name', 'Height'}, 'addDivider');
            
            try
                newDividerTag = this.MessageHandler.addDivider(varargin{:});
            catch me
                throwAsCaller(me);
            end
            
            % if nargout = 1, send out the tag of the divider added in
            if nargout > 0
                varargout{1} = newDividerTag;
            end
        end % addDivider
        
        function modifyDisplayChannel(this,varargin)
            %modifyDisplayChannel Modify the properties of the specified display channel
            %   modifyDisplayChannel(H, 'DisplayChannelTag', tag, 'Name', Value, ...)
            %   modifies the properties specified by the name-value pairs
            %   in the display channel (wave or divider) specified by
            %   the input tag. The name-value pairs must correspond to the
            %   display channel (wave or divider). The display channel cannot be
            %   moved using modifyDisplayChannel, use moveDisplayChannel instead.
            %
            %   See also dsp.LogicAnalyzer/addWave, dsp.LogicAnalyzer/addDivider,
            %            dsp.LogicAnalyzer/moveDisplayChannel,
            %            dsp.LogicAnalyzer/deleteDisplayChannel.
            
            if nargin > 1
                [varargin{:}] = convertStringsToChars(varargin{:});
            end
            
            if mod(numel(varargin), 2) == 0
                params = varargin(1:2:end);
            else
                params = varargin(2:2:end);
            end
            validateParamNames(params, {'InputChannel', 'DisplayChannelTag', 'DisplayChannel', 'Name', 'Color', 'Height', 'Radix', 'FontSize', 'Format'}, 'modifyDisplayChannel');
            
            try
                this.MessageHandler.modifyDisplayChannel(varargin{:});
            catch me
                throwAsCaller(me);
            end
        end % modifyDisplayChannel
        
        function moveDisplayChannel(this,varargin)
            %moveDisplayChannel Move the position of the specified display channel
            %   moveDisplayChannel(H, 'DisplayChannelTag', tag, 'DisplayChannel', displaychannelvalue)
            %   moves the display channel specified by the tag to the
            %   location specified by displaychannelvalue.
            %
            %   See also dsp.LogicAnalyzer/addWave, dsp.LogicAnalyzer/addDivider,
            %            dsp.LogicAnalyzer/modifyDisplayChannel,
            %            dsp.LogicAnalyzer/deleteDisplayChannel.
            
            try
                this.MessageHandler.moveDisplayChannel(varargin{:});
            catch me
                throwAsCaller(me);
            end
        end % moveDisplayChannel
        
        function deleteDisplayChannel(this, tag, varargin)
            %deleteDisplayChannel Delete the specified display channel
            %   deleteDisplayChannel(H, 'DisplayChannelTag', tag)
            %   deletes the display channel (wave or divider)
            %   specified by the input tag.
            %
            %   See also dsp.LogicAnalyzer/addWave, dsp.LogicAnalyzer/addDivider,
            %            dsp.LogicAnalyzer/moveDisplayChannel,
            %            dsp.LogicAnalyzer/modifyDisplayChannel.
            
            if nargin == 3
                tag = varargin{1};
            end
            try
                deleteDisplayChannel(this.MessageHandler, tag);
            catch me
                throwAsCaller(me);
            end
        end % deleteDisplayChannel
        
        function varargout = addCursor(this,varargin)
            %addCursor Add a cursor to the display
            %   cursorTag = addCursor(H, 'Name', Value, ...)
            %   adds in a cursor to the display. A tag value is returned
            %   which can be used to modify and delete the cursor
            %   (see modifyCursor, deleteCursor).
            %   The following name-value pairs may be passed in to this
            %   method:
            %
            %   Location Location of the cursor
            %      Specify the value of the cursor location in seconds. The
            %      default value for this property is 0.
            %   Color Color of the cursor
            %      Specify Color to be one of 'Blue' | 'Green' | 'Cyan' |
            %      'Magenta' | 'Yellow' | 'White'. The default value for
            %      this property is 'Yellow'.
            %   Locked lock the cursor
            %      Specify Locked to be one of false | true. Specifying a
            %      value of true implies that the cursor is locked (its
            %      location cannot be changed).
            %
            %   See also dsp.LogicAnalyzer/modifyCursor, dsp.LogicAnalyzer/deleteCursor.
            
            % Make sure to include Time for backwards compatibility
            validateParamNames(varargin(1:2:end), {'Name', 'Location', 'Time', 'Color', 'Locked'}, 'addCursor');
            
            try
                cursorTag = this.MessageHandler.addCursor(varargin{:});
            catch me
                throwAsCaller(me);
            end
            
            % if nargout = 1, send out the tag of the cursor added in
            if nargout > 0
                varargout{1} = cursorTag;
            end
        end
        
        function modifyCursor(this,varargin)
            %modifyCursor Modify the properties of the specified cursor
            %   modifyCursor(H, 'CursorTag', tag, 'Name', Value, ...)
            %   modifies the properties specified by the name-value pairs
            %   in the cursor specified by the input tag. The name-value
            %   pairs must correspond to a cursor.
            %
            %   See also dsp.LogicAnalyzer/addCursor, dsp.LogicAnalyzer/deleteCursor.
            
            if nargin > 1
                [varargin{:}] = convertStringsToChars(varargin{:});
            end
            try
                this.MessageHandler.modifyCursor(varargin{:});
            catch me
                throwAsCaller(me);
            end
        end
        
        function deleteCursor(this, tag, varargin)
            %deleteCursor Delete the specified cursor
            %   deleteCursor(H, tag)
            %   deletes the cursor specified by the input tag.
            %
            %   See also dsp.LogicAnalyzer/addCursor, dsp.LogicAnalyzer/modifyCursor.
            
            if nargin > 1
                tag = convertStringsToChars(tag);
            end
            
            if nargin > 2
                [varargin{:}] = convertStringsToChars(varargin{:});
            end
            try
                this.MessageHandler.deleteCursor(tag, varargin{:});
            catch me
                throwAsCaller(me);
            end
        end
        
        function displayChannelInfo = getDisplayChannelInfo(obj, varargin)
            %getDisplayChannelInfo Return settings for the Display Channel
            %   Input should be of the form 'DisplayChannelTag', tag or
            %   'DisplayChannelTag', <cell array of tags>
            %   Output is a single Display Channel setting or a cell array
            %   of the Display Channel settings.
            %
            %   See also dsp.LogicAnalyzer/getDisplayChannelTags, dsp.LogicAnalyzer/getCursorTags.
            
            if nargin > 1
                [varargin{:}] = convertStringsToChars(varargin{:});
            end
            
            try
                displayChannelInfo = obj.MessageHandler.getDisplayChannelInfo(varargin{:});
            catch me
                throwAsCaller(me);
            end
        end % getDisplayChannelInfo
        
        function cursorInfo = getCursorInfo(obj, varargin)
            %getCursorInfo Return settings for the Cursor
            %  Input should be of the form 'CursorTag', tag or
            %  'CursorTag', <cell array of tags>
            %  Output is a single Cursor setting or a cell array
            %  of the Cursor settings
            
            try
                cursorInfo = obj.MessageHandler.getCursorInfo(varargin{:});
            catch me
                throwAsCaller(me);
            end
            
        end % getCursorInfo
        
        function displayChannelTags = getDisplayChannelTags(obj)
            %getDisplayChannelTags Return all the Display Channel tags
            %   These tags can be used to modify the properties of a Display
            %   Channel.
            %
            %   See also dsp.LogicAnalyzer/getCursorTags, dsp.LogicAnalyzer/getDisplayChannelInfo.
            displayChannelTags = getDisplayChannelTags(obj.MessageHandler);
        end
        
        function cursorTags = getCursorTags(obj)
            %getCursorTags Return all the Cursor tags
            %   These tags can be used to modify the properties of a Cursor.
            %
            %   See also dsp.LogicAnalyzer/getDisplayChannelTags, dsp.LogicAnalyzer/getDisplayChannelInfo.
            cursorTags = getCursorTags(obj.MessageHandler);
        end
        
        function show(this)
            % show

            % Most webscopes work in nojvm, but LA does not connect to the
            % back end properly yet.
            if ~usejava('jvm') || ~usejava('awt')
                error(message('Spcuilib:application:ErrorNoJVMNoDisplay', getName(this)));
            end
            import matlab.internal.capability.Capability;
            lascope = this.LAModelController;
            handler = this.MessageHandler;
            
            % Pass data over to C++ to be sent to JS on
            % setSerializedSettings
            if ~handler.GraphicalSettingsStale
                lascope.GraphicalSettings = jsonencode(handler.GraphicalSettings);
            end
            if ~isempty(handler.CallMethodCache)
                lascope.CallMethodCache = jsonencode(handler.CallMethodCache);
            end
            
            inputIds = handler.InputIds;
            
            chName = getString(message('Spcuilib:webscopes:ChannelName'));
            mdl    = handler.ClientId;
            
            hasTrace = ~isempty(handler.GraphicalSettings);
            
            for indx = 1:numel(inputIds)
                sigName = sprintf('%s%d', chName, indx);
                lascope.addSignal(inputIds{indx}, {[mdl '/' sigName]}, {}, sigName, ...
                    sigName, indx, -1, false, true, 0, 1, [1 1], 0, 0, -1, jsonencode([]), 0, 0, true, hasTrace, {},false, int64(1));
            end
            %Logic Analyzer is displayed inside an IframeContainer only in
            %MATLAB online
            if ~Capability.isSupported(Capability.LocalClient)
                showInIframeContainer(this);
            else
                show@matlabshared.scopes.WebWindowSystemScope(this);
            end
            for indx = 1:100
                if this.MessageHandler.OpenComplete
                    return;
                end
                pause(.1);
            end
        end
        
        function hide(this)
            import matlab.internal.capability.Capability;
            if ~Capability.isSupported(Capability.LocalClient)
                message.publish('/web/logicAnalyzer', struct('peerNodeId', this.MessageHandler.ClientId, 'eventType', 'windowClosed'));
            else
                hide@matlabshared.scopes.WebWindowSystemScope(this);
            end
        end
        
        function delete(this)
            import matlab.internal.capability.Capability;
            % Cleanup the controller
            if ~Capability.isSupported(Capability.LocalClient)
                message.publish('/web/logicAnalyzer', struct('peerNodeId', this.MessageHandler.ClientId, 'eventType', 'windowClosed'));
            else
			    %AFTER DELETING QUEUES FORCE FLUSH OF DATA
                Simulink.sdi.internal.flushStreamingBackend();				
                Simulink.scopes.LAScope.UnRegisterForCleanupOnMLExit(this.MessageHandler.ClientId);
                if ~isempty(this.LAModelController)
                    this.LAModelController.delete;
                end
            end
        end
    end
    
    methods (Access = protected)
        
        function newObject = cloneImpl(this)
            newObject = this.loadobj(saveobj(this));
            
            if this.isVisible
                newObject.show;
            end
        end
        
        function resetImpl(this)
            
            % Only do the reset if the object has been stepped
            if this.TimeCount ~= 0
                this.LastTime  = 0;
                this.TimeCount = 0;
                this.MessageHandler.reset();
            end
        end
        
        function h = getMessageHandler(~)
            h = dsp.scopes.LogicAnalyzerMessageHandler;
        end
        
        function dataTypeObj = createDataType(~,indx,val)
            
            % Test for the input datatype and return the appropriate
            % dataType object or error out
            if isSupportedBuiltin(class(val))
                % Get the MATLAB datatype of the input value
                if islogical(val)
                    dTypeName = 'bool';
                else
                    dTypeName = class(val);
                end
                dataTypeObj = Simulink.AsyncQueue.DataType(dTypeName);
            elseif isa(val,'embedded.fi')
                % Fixed point value
                sign = val.issigned;
                wl = int32(val.WordLength);
                switch val.Scaling
                    case 'BinaryPoint'
                        fl = int32(val.FractionLength);
                        dataTypeObj = Simulink.AsyncQueue.DataType.createFixedPtBinaryPoint(sign,wl,fl);
                    case 'SlopeBias'
                        saf = val.SlopeAdjustmentFactor;
                        exp = int32(val.FixedExponent);
                        bias = val.Bias;
                        dataTypeObj = Simulink.AsyncQueue.DataType.createFixedPtSlopeBias(sign,wl,saf,exp,bias);
                end
            elseif numel(enumeration(val)) > 0 % check for enum
                if ~isValidEnum(val)
                    localError('pureEnumInput');
                end
                % create enum dataTypeObj
                [members,names] = enumeration(val);
                memberVals = int32(members);
                dataTypeObj = Simulink.AsyncQueue.DataType.createEnum(class(val),'Simulink.IntEnumType',memberVals,names);
            else
                localError('unsupportedDatatypeInput', indx , val);
            end
        end
        
        function setupAsyncQueue(this,indx,val,signalDims,isComplex,sigPath,sigUUID,sigName)
            isFrame=this.UseFrameBasedDisplay;
            aqSig = Simulink.AsyncQueue.Signal.create(this.createDataType(indx,val), int32(signalDims), isComplex, false, isFrame);
            aqSigSource = Simulink.AsyncQueue.SignalSource;
            aqSigSource.Path = sigPath;
            aqSigSource.ID = sigUUID;
            aqSigSource.Name = sigName;
            aqSigSource.Index = 1;
            aqSig.setSource(aqSigSource);
            
            if isempty(this.AsyncQueues)
                this.AsyncQueues = Simulink.AsyncQueue.Queue(aqSig);
                if isFrame
                    this.AsyncQueues.setIsFrameBased(true, this.SampleTime, length(val));
                end
            else
                this.AsyncQueues(indx) = Simulink.AsyncQueue.Queue(aqSig);
                if isFrame
                    this.AsyncQueues(indx).setIsFrameBased(true, this.SampleTime, length(val));
                end
            end
            
        end
        
        function validateInputsImpl(~,varargin)
            numInputs = numel(varargin);
            for ii = 1:numInputs
                currPortNumChans = size(varargin{ii},2);
                % G1901621 Verify that NumChannels hasn't changed and is non-zero.
                if currPortNumChans == 0
                    localError('EmptyInput', ii);
                end
                if any(any(isstruct(varargin{ii})))
                    localError('StructInput');
                end
                % validate string G1762798
                if ischar(varargin{ii}) || isstring(varargin{ii})
                    localError('unsupportedDatatypeInput', ii, class(varargin{ii}));
                end
                
                if any(any(isinf(varargin{ii}))) || any(any(isnan(varargin{ii})))
                    localError('InfNaNInput');
                end
                
                % M x N (N>1 && M>1) not supported
                sz = size(varargin{ii});
                if sz(2) > 1 && sz(1) > 1 || numel(sz) > 2
                    localError('invalidInputSize');
                end
                
                % all the inputs should have same length
                if ~isequal(sz(1),size(varargin{1},1))
                    localError('diffInputSize');
                end                
            end
        end
        function setupImpl(this, varargin)
%             % If user deleted (via call to close all force) the GUI, we
%             % need to create a new application

            if ~isScopeLaunched(this) && any(this.MessageHandler.DebugLevel >=3) && ~this.HideCalled
                show(this);
            end
            
            % Wait for the visual to open before proceeding
            for indx = 1:100
                if this.LAModelController.VisualOpen
                    break
                end
                pause(.1);
            end
            
            % Call initialize to setup any states on the Logic Analyzer
            % visual. For instance, we can set isRunning flag of
            % SystemObjectController.js to true.
            this.MessageHandler.initialize();
            
            % get default name for Channel
            chName = getString(message('Spcuilib:webscopes:ChannelName'));
            
            % Gather the information on input data types and store on visual
            numInputs = numel(varargin);
            actionFcn = 'updateSignal';
            
            % Determine data based on varargin.
            
            addSignalMsg.action = [actionFcn this.MessageHandler.ClientId];
            
            initStructValues = cell(1,numInputs);
            sigs = struct('id',initStructValues,...
                'name',initStructValues,...
                'shortName',initStructValues,...
                'isBoolean',initStructValues,...
                'isFloatingPoint',initStructValues,...
                'isComplex',initStructValues,...
                'isEnumeration',initStructValues, ...
                'enumerations',initStructValues,...
                'signalWidth',initStructValues,...
                'signalDims',initStructValues,...
                'wordLength',initStructValues...
                );
            
            inputIds = this.getInputIds;
            model = this.MessageHandler.ClientId;
            for indx = 1:numInputs
                
                % Get the input information
                
                % all the inputs should have same length
                %if ~isequal(size(varargin{indx},1),size(varargin{1},1))
                %    localError('diffInputSize');
                %end
                
                % Get signal information
                sigUUID = inputIds{indx};
                sigName = [chName num2str(indx)];
                shortName = sigName;
                sigPath = {[model,'/',sigName]};
                portIdx = indx;
                portHandle = -1;
                blockHandle = -1;
                isBoolean = islogical(varargin{indx}(1));
                isFloatingPoint = isfloat(varargin{indx}(1));
                isComplex = ~isreal(varargin{indx});
                isComplex = double(isComplex);
                isMultirate = 0;
                [isEnumeration, enumValues] = getEnumerationInfo(varargin{indx}(1));
                if isFloatingPoint || isBoolean || isEnumeration
                    wordLength = 0;
                else
                    if isprop(varargin{indx}(1), 'WordLength')
                        wordLength = varargin{indx}.WordLength;
                    else
                        try
                            temp = numerictype(class(varargin{indx}(1)));
                        catch ME %#ok
                            temp = numerictype(varargin{indx}(1));
                        end
                        wordLength = temp.WordLength;
                    end
                end
                sz = size(varargin{indx});
                signalWidth = prod(sz(2:end));
                signalDims = [1 sz(end)];
                isCommented = 0;
                hasTrace = ~isempty(this.MessageHandler.GraphicalSettings);
                % Create an async queue per signal
                this.setupAsyncQueue(indx,varargin{indx},signalDims,isComplex,sigPath,sigUUID,sigName);
                
                % LA wants the complex in the format of 1 element per
                % channel where 0 means no imaginary, 1 is real of a
                % complex and 2 is imaginary of a complex
                if isComplex
                    isComplex = repmat([1 2], 1, signalWidth);
                else
                    isComplex = zeros(1, signalWidth);
                end
                
                % Bind signal with server
                this.LAModelController.addSignal(sigUUID,sigPath,{},sigName,shortName,...
                    portIdx,portHandle,isBoolean,isFloatingPoint,...
                    isComplex,signalWidth,signalDims,wordLength,isMultirate,blockHandle,jsonencode(enumValues), double(isEnumeration),isCommented,true,hasTrace,{},false,this.AsyncQueues(indx).SignalSourceID);
                
                % Create signal array for binding with client
                sigs(indx).id = sigUUID;
                sigs(indx).name = sigName;
                sigs(indx).shortName = sigName;
                sigs(indx).isBoolean = double(isBoolean);
                sigs(indx).isFloatingPoint = double(isFloatingPoint);
                sigs(indx).isComplex = isComplex;
                sigs(indx).isEnumeration = isEnumeration;
                sigs(indx).enumerations = {enumValues};
                sigs(indx).signalWidth = signalWidth;
                sigs(indx).signalDims = signalDims;
                sigs(indx).wordLength = wordLength;
                sigs(indx).isMultirate = 0;
            end
            
            % Publish message to client
            addSignalMsg.params.signals = sigs;
            message.publish('/logicanalyzer',addSignalMsg);
            
            % Start
            this.LAModelController.start(0); % Will it always be 0 or can I get the start time?
            
            % Publish onSimStart message to client. This is needed to set
            % the simStatus and the trigger information.
            simStartMsg.action = ['onSimStart' this.MessageHandler.ClientId];
            simStartMsg.params = [];
            message.publish('/logicanalyzer',simStartMsg);
            
            % Add observers to the async queues
            this.addAsyncQueueObservers(varargin{:});
            Simulink.scopes.LAScope.RegisterForCleanupOnMLExit(this.MessageHandler.ClientId,this);
            licenseCheckout;
        end

        function addAsyncQueueObservers(this,varargin)
            
            numInputs = numel(varargin);
            
            % Add observers to the async queues
            for idx = 1:numInputs
                % DB observer is composite so we need only one per AsyncQueue
                addDbObserver();
                signalWidth = size(varargin{idx}, 2);
                for channelIdx=1:signalWidth
                    addObserversImpl(false);
                    if ~isreal(varargin{idx})
                        addObserversImpl(true)
                    end
                end
                Simulink.AsyncQueue.Queue.configureQueuesAndLaunchThreads(...
                    this.AsyncQueues(idx));
            end
            
            function addObserversImpl(observeImagPart)
                
                laclient = Simulink.AsyncQueue.SignalClient;
                laclient.ObserverType = 'logicanalyzer_observer';
                laclient.ObserveImaginaryPart = observeImagPart;
                laclient.DimensionChannel = channelIdx;
                laclient.ObserverParams.ClientID = this.MessageHandler.ClientId;
                laclient.ObserverParams.PreBufferingOn = false;
                laclient.ObserverParams.Adaptors.Name{1} = 'simviz_LA_Trigger';
                laclient.ObserverParams.Adaptors.Name{2} = 'simviz_LA_min_and_max_thinner';
                this.AsyncQueues(idx).bindClient(laclient);
                
            end
            
            function addDbObserver()
                dbclient = Simulink.AsyncQueue.SignalClient;
                dbclient.ObserverType = 'database_observer';
                this.AsyncQueues(idx).bindClient(dbclient);
            end
        end
        
        function releaseImpl(this)
            
            handler = this.MessageHandler;
            handler.TerminateComplete = false;
            % Release resources, such as file handles
            numInputs = this.getNumInputs;
            for indx=1:numInputs
                delete(this.AsyncQueues(indx));
            end
            this.LAModelController.terminate(this.LastTime);
            this.LastTime = 0;
            this.TimeCount = 0;
            while ~handler.TerminateComplete
                pause(.1);
            end
            %AFTER DELETING QUEUES FORCE FLUSH OF DATA
            Simulink.sdi.internal.flushStreamingBackend();
            Simulink.scopes.LAScope.UnRegisterForCleanupOnMLExit(this.MessageHandler.ClientId);
        end
        
        function updateImpl(this, varargin)
            for ii = 1:numel(varargin)
                if any(any(isinf(varargin{ii}))) || any(any(isnan(varargin{ii})))
                    localError('InfNaNInput');
                end
            end
            if isempty(this.AsyncQueues)
                setupImpl(this, varargin{:});
            end
            
            t = this.TimeCount * this.SampleTime;
            for idx = 1:numel(varargin)
                val = varargin{idx};
                if isenum(val) && isValidEnum(val)
                    val = int32(val);
                elseif isfi(val)
                    val = convertIfFiIsBuiltinInt(val);
                end
                if this.UseFrameBasedDisplay
                    this.AsyncQueues(idx).write(t, val);
                    tj = t + this.SampleTime; % sample time == frame time
                else                   
                    for jdx = 1:size(val, 1)
                        % INTERP T for frames
                        tj = t+(jdx-1)*this.SampleTime;
                        this.AsyncQueues(idx).write(tj, val(jdx, :));
                    end
                end
            end
            this.TimeCount = this.TimeCount + size(val, 1);
            this.LastTime = tj;
            this.LastWriteTime = tic;
        end
        
        function S = saveObjectImpl(obj)
            S = saveObjectImpl@matlabshared.scopes.WebWindowSystemScope(obj);
            S.SampleTime    = obj.SampleTime;
            S.Name          = obj.Name;
            S.NumInputPorts = obj.NumInputPorts;
            S.Visible       = obj.isVisible;
            %             S.TimeSpan      = obj.TimeSpan;
        end
        
        function loadObjectImpl(obj, S, wasLocked)
            licenseCheckout;
            if isfield(S, 'LogicAnalyzerProperties')
                sProps = S.LogicAnalyzerProperties;
                if isfield(sProps, 'DisplayChannelHeight')
                    sProps.DisplayChannelHeight = 12*sProps.DisplayChannelHeight;
                end
                if isfield(sProps, 'DisplayChannelSpacing')
                    sProps.DisplayChannelSpacing = 4*sProps.DisplayChannelSpacing;
                end
                sPropsFields = fieldnames(sProps);
                sPropsFields(strcmp(sPropsFields, 'ReduceUpdates')) = [];
                for ii = 1:numel(sPropsFields)
                    obj.(sPropsFields{ii}) = sProps.(sPropsFields{ii});
                end
                return;
            end
            
            obj.Name          = S.Name;
            obj.SampleTime    = S.SampleTime;
            obj.NumInputPorts = S.NumInputPorts;
            
            loadObjectImpl@matlabshared.scopes.WebWindowSystemScope(obj, S, wasLocked);
            
            % Update call method cache for TimeSpan and TimeDIsplayOffset
            mh = S.MessageHandler;
            if mh.ClientSettingsStale
                timeSpan  = mh.TimeSpan;
                startTime = mh.TimeDisplayOffset;
            else
                timeSpan  = mh.ClientSettings.timeRange;
                startTime = mh.ClientSettings.startTime;
            end
            obj.callMethod('setTimeSpan',  timeSpan);
            obj.callMethod('setStartTime', startTime);
            
            if S.Visible
                obj.show;
            end
        end
        
        function flag = isInputSizeMutableImpl(~,~)
            % allow different sizes of inputs to come in (all inputs should be
            % the same size though). first call to stepImpl determines the max
            % size that can be sent in.
            flag = true;
        end
        
        function flag = isInactivePropertyImpl(obj, prop) %#ok<INUSD>
            flag = false;
        end
        
        function num = getNumInputsImpl(obj)
            num = obj.NumInputPorts;
        end
        
        function time = getCurrentTime(this)
            time = this.TimeCount * this.SampleTime;
        end
    end % protected methods
    
    methods(Hidden)
        function name = getName(this)
            name = this.Name;
        end
        
        function inputIds = getInputIds(this)
            inputIds = this.MessageHandler.InputIds;
        end
        function showInIframeContainer(this, varargin)
            %showInIframeContainer Display LogicAnalyzer inside an IframeContainer in MATLAB online
            messageHandler = this.MessageHandler;
            url = messageHandler.getUrl;
            clientID = messageHandler.ClientId;
            feature = messageHandler.DebugLevel;
            if any(feature == [0 1 4])
                url = [url '-debug'];
            end
            url = [url '.html?ClientID=' clientID];
            url = connector.getUrl(url);
            message.publish('/web/logicAnalyzer', struct('peerNodeId', this.MessageHandler.ClientId, 'eventType', 'show', 'url', url));
            message.publish('/web/logicAnalyzer', struct('peerNodeId', this.MessageHandler.ClientId, 'name', getName(this), 'eventType', 'windowPropertyChanged'));
        end
    end % hidden methods
    
    methods(Static,Hidden,Access=protected)
        function groups = getPropertyGroupsImpl
            props = {...
                'Name', ...
                'SampleTime', ...
                'Position', ...
                'TimeSpan', ...
                'TimeDisplayOffset', ...
                'NumInputPorts', ...
                'DisplayChannelRadix', ...
                'DisplayChannelHeight', ...
                'DisplayChannelColor', ...
                'DisplayChannelFormat', ...
                'DisplayChannelFontSize', ...
                'DisplayChannelSpacing', ...
                'BackgroundColor'};
            groups = matlab.system.display.Section('Title', 'Parameters',...
                'PropertyList', props);
        end
    end % static, protected, hidden methods
    
    methods(Static, Hidden)
        function validateCellObject(value, objType, msgID)
            % validate that value is a cell array of the specified object; if
            % not, use the input message id to error.
            if (~isa(value, 'cell') || ...
                    ~(all(cellfun(@(x) isa(x,objType), value)))) && ...
                    ~isempty(value)
                localError(msgID);
            end
        end
        function fevalHandler(action, ~, varargin)
            switch action
                case 'HelpRequested'
                    switch varargin{1}
                        case 'aboutDST'
                            aboutdspsystbx;
                        case 'logicAnalyzer'
                            helpview('dsp', 'logicanalyzersystemobject');
                        otherwise
                            helpview('dsp', varargin{1});
                    end
            end
        end
    end % static, hidden methods
end % LogicAnalyzer

function validateParamNames(paramNames, validNames, method)

if isempty(validNames)
    return
end
isInvalid = cellfun(@(c) ~any(strcmp(c, validNames)), paramNames);
firstInvalid = find(isInvalid, 1, 'first');
if ~isempty(firstInvalid)
    id = 'dspshared:LogicAnalyzer:InvalidParameterNameToMethod';
    throwAsCaller(MException(id, getString(message(id, paramNames{firstInvalid}, method))));
end

end

% local functions
function localError(ID, varargin)
error(message(['dspshared:LogicAnalyzer:' ID], varargin{:}));
end % localError

function licenseCheckout
Simulink.scopes.LAScope.checkoutLicense;
end % licenseCheckout
% end local functions

function result = isValidEnum(enumClass)
result = false;
% get meta class
enumMetaClass = meta.class.fromName(class(enumClass));
if ~isempty(enumMetaClass.EnumerationMemberList)
    if validNumericalSuperclass(enumMetaClass)
        if isreal(enumeration(enumMetaClass.Name))
            result = true;
        end
    end
end
end

% modified superclass test from isSupportedEnumClass
function result = validNumericalSuperclass(enumMetaClass)
% Only support direct single-inheritance
result = (isscalar(enumMetaClass.SuperclassList) && ...
    ismember(enumMetaClass.SuperclassList.Name, ...
    {'Simulink.IntEnumType', ...
    'int8', ...
    'int16', ...
    'int32', ...
    'uint8', ...
    'uint16',...
    'uint32',...
    'logical',...
    'single',...
    'double'}));
end

% list of supported data types for LA
function result = isSupportedBuiltin(class)
result = ismember(class, ...
    {'int8', ...
    'int16', ...
    'int32', ...
    'uint8', ...
    'uint16',...
    'uint32',...
    'logical',...
    'single',...
    'double'});
end

function [isEnum, enumInfo] = getEnumerationInfo(value)
enumInfo = [];
isEnum = isenum(value);
if isEnum
    [v, enumInfo.strings] = enumeration(value);
    enumInfo.values = abs(v);
end
end

% Check to see if the input fi can be converted (with precision loss) to an
% equivalent builtin integer type and do so if its possible
function outVal = convertIfFiIsBuiltinInt(fiVal)
wl = fiVal.WordLength;
slope = fiVal.Slope;
bias= fiVal.Bias;
isbuiltinint = @(wl)isequal(wl,8) || isequal(wl,16) || isequal(wl,32) || isequal(wl,64);
if isbuiltinint(wl) && isequal(slope,1) && isequal(bias,0)
    signPrefix = '';
    if ~issigned(fiVal)
        signPrefix = 'u';
    end
    intType = [signPrefix,'int',num2str(wl)];
    outVal = cast(fiVal,intType);
else
    outVal = fiVal;
end

end

% [EOF]
