classdef configBase < handle
    %XCP.BLKMASKS.CONFIGBASE
    %   XCP.BLKMASKS.CANCONFIG and XCP.BLKMASKS.UDPConfig inherit from this
    %   base class

%   Copyright 2018-2023 The MathWorks, Inc.

    properties(SetObservable=true)
        % Mask Parameters
        SlaveName = '';
        A2LFile = '';
        SlaveID = 0;
        EnableSecurity = false;
        SeedKeyLib = '';
        EnableStatus = false;
        SampleTime = 0;

        % Generic Properties
        BlockHandle;
        BlockName;
        DialogHandle;
        CloseListener;
    end
    
    %% Constructor Block
    methods
        function obj = configBase(block)
            
            % Set the Generic Props
            obj.BlockName   = block;
            obj.BlockHandle = get_param(block, 'handle');
            
            %Set the Mask Properties based on Block Properties
            obj.SlaveName = get_param(block,'SlaveName');
            obj.A2LFile = get_param(block,'A2LFile');
            obj.SlaveID = get_param(block,'SlaveID');
            obj.EnableSecurity = xcp.blkmasks.utils.onOff2Logical(get_param(block,'EnableSecurity'));
            obj.SeedKeyLib = get_param(block,'SeedKeyLib');
            obj.EnableStatus = xcp.blkmasks.utils.onOff2Logical(get_param(block,'EnableStatus'));
            obj.SampleTime = get_param(block,'SampleTime');
            
        end
    end
    
    %% DDG Dialog Schema
    methods
        function dlgstruct = getDialogSchema(obj) 

            configname.Name           = message('xcp:xcpblks:ConfigName').getString();
            configname.Type           = 'edit';
            configname.Tag            = 'configname_tag';
            configname.MatlabArgs     = {obj,'%value'};
            configname.MatlabMethod   = 'setConfigName';
            configname.Value = obj.SlaveName;
            configname.RowSpan        = [1 1];
            configname.ColSpan        = [1 16];
            
            a2lbrowse.Type = 'pushbutton';
            a2lbrowse.Name = message('xcp:xcpblks:Browse').getString();
            a2lbrowse.Tag = 'a2lbrowse_tag';
            a2lbrowse.RowSpan = [2,2];
            a2lbrowse.ColSpan = [14 16];
            a2lbrowse.MatlabArgs = {obj};
            a2lbrowse.MatlabMethod = 'a2LBrowseCB';
            a2lbrowse.DialogRefresh = 1;
            
            a2ledit.Type = 'edit';
            a2ledit.Name = message('xcp:xcpblks:A2LFile').getString();
            a2ledit.Tag = 'a2ledit_tag';
            a2ledit.RowSpan = [2 2];
            a2ledit.ColSpan = [1 13];
            a2ledit.MatlabArgs = {obj, '%value'};
            a2ledit.MatlabMethod = 'setA2LFile';
            a2ledit.Value = obj.A2LFile;
            
            securitybox.Type = 'checkbox';
            securitybox.Name = message('xcp:xcpblks:EnableSecurity').getString();
            securitybox.Tag = 'securitybox_tag';
            securitybox.RowSpan = [3 3];
            securitybox.ColSpan = [1 3];
            securitybox.MatlabArgs = {obj,'%value'};
            securitybox.MatlabMethod = 'setSecurity';
            securitybox.Value = obj.EnableSecurity;
            securitybox.DialogRefresh = true;
            securitybox.Enabled = true;
            % Perform a Windows OS check. Linux does not support SeedNKey yet.
            if ~ispc()
                securitybox.Visible = false;
            end 
            
            securitytext.Type = 'text';
            securitytext.Enabled = obj.EnableSecurity;
            securitytext.Tag = 'securitytext_tag';
            securitytext.RowSpan = [4 4];
            securitytext.ColSpan = [1 1];
            % Windows and Linux have different Seed/Key file format. 
            % Enable only on Windows. 
            if ispc()
                securitytext.Name = message('xcp:xcpblks:FileDLL').getString(); 
            else 
                securitytext.Name = message('xcp:xcpblks:FileSO').getString();
                securitytext.Visible = false;
            end
            
            securityedit.Type = 'edit';
            securityedit.Name = '';
            securityedit.Tag = 'securityedit_tag';
            securityedit.RowSpan = [4 4];
            securityedit.ColSpan = [2 13];
            securityedit.MatlabArgs = {obj, '%value'};
            securityedit.MatlabMethod = 'setSeedKeyLib';
            securityedit.Value = obj.SeedKeyLib;
            securityedit.Enabled = obj.EnableSecurity;
            if ~ispc()
                % Enable only under Windows for now. 
                securityedit.Visible = false;
            end
            
            securitybrowse.Type = 'pushbutton';
            securitybrowse.Name = message('xcp:xcpblks:Browse').getString();
            securitybrowse.Tag = 'securitybrowse_tag';
            securitybrowse.RowSpan = [4 4];
            securitybrowse.ColSpan = [14 16];
            securitybrowse.MatlabArgs = {obj};
            securitybrowse.MatlabMethod = 'seedKeyBrowseCB';
            securitybrowse.Enabled = obj.EnableSecurity;
            securitybrowse.DialogRefresh = true;
            if ~ispc()
                % Enable only under Windows for now. 
                securitybrowse.Visible = false;
            end     
            
            connstatus.Type = 'checkbox';
            connstatus.Name = message('xcp:xcpblks:OutputConnStatus').getString();
            connstatus.Tag = 'connstatus_tag';
            connstatus.RowSpan = [5 5];
            connstatus.ColSpan = [1 5];
            connstatus.MatlabArgs = {obj,'%value'};
            connstatus.MatlabMethod = 'setEnableStatus';
            connstatus.Value = obj.EnableStatus;
            connstatus.DialogRefresh = true;

            sampletime.Type = 'edit';
            sampletime.Name = message('xcp:xcpblks:ST').getString();
            sampletime.Tag = 'sampletime_tag';
            sampletime.RowSpan = [8 8];
            sampletime.ColSpan = [1 16];
            sampletime.MatlabArgs = {obj,'%value'};
            sampletime.MatlabMethod = 'setSampleTime';
            sampletime.Value = obj.SampleTime;

            ParametersPanel.Type = 'group';
            ParametersPanel.Name = message('xcp:xcpblks:Params').getString();
            ParametersPanel.Tag = 'ParametersPanel_tag';
            ParametersPanel.LayoutGrid = [10 16];
            ParametersPanel.Items = {configname, a2ledit, a2lbrowse, securitybox, securitytext,...
                                     securityedit, securitybrowse, connstatus, sampletime};
            
            dlgstruct.DialogTitle = getString(message('xcp:xcpblks:blockParameters', get_param(obj.BlockHandle,'Name')));
            dlgstruct.StandaloneButtonSet = {'OK','Apply','Cancel','Help'};
            dlgstruct.EmbeddedButtonSet = {'OK','Apply','Cancel','Help'};
            dlgstruct.Items = {ParametersPanel};
            dlgstruct.PreApplyMethod = 'PreApplyMethod';
            
            if (strcmpi(get_param(bdroot(obj.BlockName),'BlockDiagramType'),'Library'))
                %When in library disable dialog
                dlgstruct.DisableDialog=true;
                return
            end
            
            % Disable the dialog if we're in External mode and connected or
            % running.  SimulationStatus is 'external' if we're either
            % connected or running in external mode.
            mode = get_param( bdroot(obj.BlockName), 'SimulationMode' );
            status = get_param( bdroot(obj.BlockName), 'SimulationStatus' );
            if strcmpi( mode, 'external' ) && ~strcmpi( status, 'stopped' )
                dlgstruct.DisableDialog = true;
            end
        end
    end
    
    %% Set Methods/Callbacks
    methods
        function PreApplyMethod(obj)
            if (strcmp(get_param(bdroot(obj.BlockName),'BlockDiagramType'),'library'))
                return; %Don't do anything
            end
            if ~isempty(obj.A2LFile)
                a2lObj = xcpA2L(obj.A2LFile);
                xcp.blkmasks.configBase.validateA2L(obj.BlockHandle, a2lObj);
            end
            set_param(obj.BlockHandle,'SlaveName', obj.SlaveName, ...
                                      'A2LFile'  , obj.A2LFile, ...
                                      'EnableSecurity', xcp.blkmasks.utils.logical2OnOff(obj.EnableSecurity),...
                                      'SeedKeyLib', obj.SeedKeyLib,...
                                      'EnableStatus', xcp.blkmasks.utils.logical2OnOff(obj.EnableStatus),...
                                      'SampleTime', obj.SampleTime);
        end

        function setSampleTime(obj,value)
           obj.SampleTime = value; 
        end
        
        function setEnableStatus(obj,value)
            obj.EnableStatus = value;
        end

        function seedKeyBrowseCB(obj)
            % Open the dialog to select the shared library.
            [filename, pathname] = uigetfile({'*.dll', 'Seed&Key Library(*.dll)'},...
                'Select a Seed&Key External Library(DLL)', obj.SeedKeyLib);

            % Check if a selection was made.
            if ~(isequal(filename,0) || isequal(pathname,0))
                % Set the value on the block dialog.
                %dialog.setWidgetValue(tags.SeedKeyLib, [pathname filename]);
                obj.SeedKeyLib = [pathname filename];
            end
        end
        
        function setSeedKeyLib(obj, value)
            obj.SeedKeyLib = value;
        end
        
        function setConfigName(obj,value)
            obj.SlaveName = value;
        end
        
        function setSecurity(obj,value)
            if ~ispc()
                error(message('xcp:xcpblks:noSeedKeySupportLinux').getString());
            end
            obj.EnableSecurity = value;
        end
        
        function a2LBrowseCB(obj)
            % Open the dialog to select the A2L file.
            [filename, pathname] = uigetfile({'*.a2l', 'A2L File(*.a2l)'},...
                                    'Select an A2L file', obj.A2LFile);
            
            % Check if a selection was made.                     
            if ~(isequal(filename,0) || isequal(pathname,0))
                obj.setA2LFile(fullfile(pathname,filename));
            end
        end
        
        function setA2LFile(obj, fullPath)
            
            if isempty(fullPath)
                obj.A2LFile = fullPath;
                return;
            end
            
            % Check for A2L file validity
            success = false;
            try
                xcpA2L(fullPath);
                success = true;
            catch %#ok<CTCH>
                % Do nothing.
            end

            if success
                % Verify if a file with A2L extension was provided.
                obj.A2LFile = fullPath;
            else % Invalid A2L file specified.
                err = message('xcp:xcpblks:InvalidA2LFile', fullPath);
                errTitle = message('xcp:xcpblks:XCPErrorTitle');
                uiwait(errordlg(err.string, errTitle.string, 'modal'));
            end
        end

        %Helper Functions
        function installCloseListener(obj)
            mdl = get_param(bdroot(obj.BlockHandle), 'Object');
            obj.CloseListener = Simulink.listener(mdl, 'CloseEvent', ...
                @(src,evt)xcp.blkmasks.configBase.modelCloseListener(src,evt,obj));
        end
        
    end

   %% Static Methods
   methods (Static = true)

		function modelCloseListener(~, ~, obj)
			obj.DialogHandle.delete;
			obj.delete;
		end
        
		function index = getCTRSchemeIndex(schemeString)
			index = 0;
			switch schemeString
				case 'One counter for all CTOs and DTOs'
					index = 1;
				case 'Separate counters for (RES,ERR,EV,SERV) and (DAQ)'
					index = 2;
				case 'Separate counters for (RES,ERR), (EV,SERV) and (DAQ)'
					index = 3;	
			end	
        end
 
        function [protocolInfo, transportInfo, daqInfo, eventsInfo, slaveTag, tlTag, measurementsInfoJSON, compuMethodInfoJSON, compuTabInfoJSON] = ...
                    getConfigParams
                
            %XCP.BLKMASKS.CONFIGBASE.GETCONFIGPARAMS pulls configuration parameters from the A2L
            %
            %    [PROTOCOLINFO, TRANSPORTINFO, DAQINFO, EVENTS, MEASUREMENTS] = 
            %    GETCONFIGPARAMS returns the required parameters XCP
            %    Configuration block

            % Get current block name and handle
            blk = gcb;
            blkh = gcbh;
            TLTypeStr = xcp.blkmasks.utils.getTLTypeStr(blkh);

            % Initialize all output parameters
            transportInfo = [];
            measurementsInfoJSON = [];
            compuMethodInfoJSON = [];
            compuTabInfoJSON = [];
            simStatus = get_param(bdroot(gcb), 'SimulationStatus');
            if strcmpi(simStatus, 'stopped')
                protocolInfo = 0;
                transportInfo = 0;
                daqInfo = 0;
                eventsInfo = 0; 
                slaveTag = '';
                tlTag = '';
                return;
            end
            
            % Parse A2L file.
            fileName = get_param(gcbh, 'A2LFile');
            if isempty(fileName)
                error(message('xcp:xcpblks:NoA2LSpecified', TLTypeStr,blk));
            end
            a2lObj = xcpA2L(fileName);
            xcp.blkmasks.configBase.validateA2L(blkh, a2lObj);

            % Block Information
            configName = get_param(blkh, 'SlaveName');
            slaveTag = strcat(bdroot(blk), ['_',TLTypeStr,'Slave_'], configName);
            switch TLTypeStr
                case 'UDP'
                    tlTag = strcat(bdroot(blk), '_UDPTL_', configName); %Need a unique tlTag for each UDP Configuration
                case 'CAN'
                    tlTag = strcat(bdroot(blk), '_CANTL'); %There can only be one CAN TL in a model 
                case 'CAN FD'
                    tlTag = strcat(bdroot(blk), '_CANFDTL'); %There can only be one CAN FD TL in a model 
                otherwise
                    assert(false,'Internal Error: Invalid Use Case');
            end
            % Protocol Information - [TIMEOUTS MAXCTO/DTO BYTEORDER ADDRGRAN]
            commonParams = a2lObj.getCommonParameters(TLTypeStr);
            protocolStruct = commonParams.ProtocolLayer;

            protocolInfo = [1 double(protocolStruct.T1) ...
                            double(protocolStruct.T2) ...
                            double(protocolStruct.T3) ...
                            double(protocolStruct.T4) ...
                            double(protocolStruct.T5) ...
                            double(protocolStruct.T6) ...
                            double(protocolStruct.T7) ...
                            double(protocolStruct.MaxCTO) ...
                            double(protocolStruct.MaxDTO) ...
                            double(protocolStruct.ByteOrder), ...
                            double(protocolStruct.AddressGranularity)];
                        
            if(strcmp(TLTypeStr,'CAN') && protocolStruct.MaxDTO ~= 8)
                %Otherwise would be hard to diagnose if customer used wrong
                %MAX_DTO with CAN TL
                error(message('xcp:xcpblks:maxDTOCANViolated',protocolStruct.MaxDTO));
            end
       
            % Transport Layer Information
            if(strcmp(TLTypeStr,'CAN')) % CAN Transport Layer
                canInfo = a2lObj.getCANInfo();
                numDAQ = 0;
                daqCANIDs = 0;
                % Append extended ID flag to the MSB of CANID passed into s-functions
                canIDMaster = canInfo.CANIDClient;
                if canInfo.CANIDClientIsExtended
                    canIDMaster = canIDMaster + 2^31;
                end
                canIDSlave = canInfo.CANIDServer;
                if canInfo.CANIDServerIsExtended
                    canIDSlave = canIDSlave + 2^31;
                end
                
                transportInfo = [1, ...
                                 -1, ... % transportStruct.CANIDBroadcast
                                 double(canIDMaster), ...
                                 double(canIDSlave), ...
                                 double(canInfo.BaudRate), ...
                                 double(canInfo.SampleRate), ...
                                 double(canInfo.MaxDLCRequired), ...
                                 numDAQ, ...
                                 daqCANIDs];
            elseif(strcmp(TLTypeStr, 'CAN FD')) % CAN FD Transport layer
                canfdInfo = a2lObj.getCANInfo();
                numDAQ = 0;
                daqCANIDs = 0;
                % Append extended ID flag to the MSB of CANID passed into s-functions
                canIDMaster = canfdInfo.CANIDClient;
                if canfdInfo.CANIDClientIsExtended
                    canIDMaster = canIDMaster + 2^31;
                end
                canIDSlave = canfdInfo.CANIDServer;
                if canfdInfo.CANIDServerIsExtended
                    canIDSlave = canIDSlave + 2^31;
                end

                if isempty(canfdInfo.CANFD) % g2881087
                    error(message('xcp:xcpblks:noCANFDInfo', TLTypeStr,blk));
                end
                
                transportInfo = [1, ...
                                 -1, ... % transportStruct.CANIDBroadcast
                                 double(canIDMaster), ...
                                 double(canIDSlave), ...
                                 double(canfdInfo.BaudRate), ...
                                 double(canfdInfo.SampleRate), ...
                                 double(canfdInfo.CANFD.MaxDLCRequired), ...
                                 numDAQ, ...
                                 double(canfdInfo.CANFD.MaxDLC), ...
                                 double(canfdInfo.CANFD.BaudRate),...
                                 daqCANIDs];
            elseif(strcmp(TLTypeStr,'UDP'))% UDP Transport Layer
                udpInfo = a2lObj.getUDPInfo();
                transportInfo = [1, ...
                    xcp.ipaddr(udpInfo.AddressString), ...
                    double(udpInfo.Port)];
            else
                assert(false,'Internal Error: Invalid Use Case');
            end
            
            % DAQ Information
            daqStruct = commonParams.DAQ;

            % Granularity & maxODTEntrySize
            granularityODTEntryDAQ = double(daqStruct.GranularityODTEntrySizeDAQ);
            maxODTEntrySizeDAQ = double(daqStruct.MaxODTEntrySizeDAQ);
            if ~isempty(daqStruct.STIM)
                granularityODTEntrySTIM = double(daqStruct.STIM.GranularityODTEntrySizeSTIM);
                maxODTEntrySizeSTIM = double(daqStruct.STIM.MaxODTEntrySizeSTIM);
            else
                %Use daq granularity instead
                granularityODTEntrySTIM = granularityODTEntryDAQ;
                %Use maxODTDAQ instead
                maxODTEntrySizeSTIM = maxODTEntrySizeDAQ;
            end
            
            % Overload indication.
            overload = double(daqStruct.OverloadIndication);
            
            % Timestamp
            if ~isempty(daqStruct.Timestamp)
            	timestampTicks = double(daqStruct.Timestamp.Ticks);
                timestampSize = double(daqStruct.Timestamp.Size);
                timestampUnit = double(daqStruct.Timestamp.Unit);
            else
                timestampTicks = 0;
                timestampSize = double(xcp.a2l.TimestampSize.NO_TIME_STAMP);
                timestampUnit = 0;
            end

            daqInfo = [1, ...
                       double(daqStruct.ConfigType), ...
                       overload, ...
                       double(daqStruct.MaxDAQ), ...
                       double(daqStruct.MaxEventChannels), ...
                       double(daqStruct.MinDAQ), ...
                       double(daqStruct.OptimizationType), ...
                       double(daqStruct.AddressExtension), ...
                       double(daqStruct.IdentificationFieldType), ...
                       granularityODTEntryDAQ, ...
                       maxODTEntrySizeDAQ, ...
                       granularityODTEntrySTIM, ...
                       maxODTEntrySizeSTIM, ...
                       -1, ... % STIM bit in STIM struct
                       timestampTicks, ...
                       timestampSize,  ...
                       timestampUnit];

            % Events 
            eventNames = a2lObj.getEventNames(TLTypeStr);
            numEvents = length(eventNames);
            eventsInfo = zeros(1, numEvents*6, "double");

            for idx = 1:numEvents
                % Get Event structure.
                eventStruct = a2lObj.getEventInfoTLType(eventNames{idx}, TLTypeStr);
                eventsInfo((idx-1)*6+1) = double(eventStruct.ChannelNumber);
                eventsInfo((idx-1)*6+2) = double(eventStruct.Direction);
                eventsInfo((idx-1)*6+3) = double(eventStruct.MaxDAQList);
                eventsInfo((idx-1)*6+4) = double(eventStruct.ChannelTimeCycle);
                eventsInfo((idx-1)*6+5) = double(eventStruct.ChannelTimeUnit);
                eventsInfo((idx-1)*6+6) = double(eventStruct.ChannelPriority);
            end
            eventsInfo = [numEvents eventsInfo];

            % Measurements
            measurementNames = a2lObj.Module.Measurements.keys';
           
            %Extract array of measurement structs and make sure they have
            %the same order as the measurement names.
            MeasurementInfo = a2lObj.Module.Measurements.toArray();
            [~, indices] = sortrows(measurementNames);
            MeasurementInfo(indices) = MeasurementInfo(:);

            %Convert measurement, compu methods and compu tabs to JSON            
            measurementsStruct = struct("Name", {},...
                                        "DataType", {},...
                                        "Conversion",{}, ... % Conversion is a string.
                                        "Resolution",{}, ...
                                        "Accuracy",{}, ...
                                        "LowerLimit", {}, ...
                                        "UpperLimit", {},... % Scaling Unit % Rate
                                        "ECUAddress",{}, ...
                                        "ByteOrder", {},...
                                        "Layout",{}, ...
                                        "Dimension",{});

            CompuMethodStruct =   struct("Name", {},...
                                        "ConversionType", {},...
                                        "CompuTabRef", {},...
                                        "Coeffs", {},...
                                        "CoeffsLinear", {});
                                                
            CompuTabStruct =     struct("Name", {},...
                                        "ConversionType", {},...
                                        "ClassType", {}, ...
                                        "DefaultValue", {},...
                                        "DefaultValueNumeric", {},...
                                        "InVal", {},...
                                        "OutVal", {},...
                                        "InValMin", {},...
                                        "InValMax", {},...
                                        "InTab", {},...
                                        "OutTab", {});
            
            for i=1:length(MeasurementInfo)
                dimension = [1 1 1];
                if length(MeasurementInfo(i).dimension()) > 3
                    continue
                end
                measurementsStruct(i).Name                          = MeasurementInfo(i).Name;
                measurementsStruct(i).DataType                      = MeasurementInfo(i).LocDataType;
                measurementsStruct(i).Conversion                    = MeasurementInfo(i).ConversionName;
                measurementsStruct(i).Resolution                    = MeasurementInfo(i).Resolution;
                measurementsStruct(i).Accuracy                      = MeasurementInfo(i).Accuracy;
                measurementsStruct(i).LowerLimit                    = MeasurementInfo(i).LowerLimit;
                measurementsStruct(i).UpperLimit                    = MeasurementInfo(i).UpperLimit;
                if ~isempty(double(MeasurementInfo(i).ECUAddress))
                    measurementsStruct(i).ECUAddress                = MeasurementInfo(i).ECUAddress;
                else
                    measurementsStruct(i).ECUAddress                = 0; 
                end
                measurementsStruct(i).ByteOrder                     = MeasurementInfo(i).ByteOrder;
                measurementsStruct(i).Layout                        = MeasurementInfo(i).Layout;
                dimension(1:length(MeasurementInfo(i).dimension())) = MeasurementInfo(i).dimension()';
                measurementsStruct(i).Dimension                     = dimension; 
            end 
            
            compuMethods = a2lObj.Module.CompuMethods.toArray();
            for i=1:length(compuMethods)
                CompuMethodStruct(i).Name                           = compuMethods(i).Name;
                %We convert the conversion type for any CompuMethod with Formula type to Identical and throw the warning
                %when the same measurement is selected on the block mask
                if strcmpi(compuMethods(i).ConversionType, "FORM")
                    CompuMethodStruct(i).ConversionType             = xcp.a2l.ConversionType("IDENTICAL");                   
                else
                    CompuMethodStruct(i).ConversionType             = compuMethods(i).ConversionType;
                end                
                CompuMethodStruct(i).CompuTabRef                    = compuMethods(i).CompuTabRef;
                CompuMethodStruct(i).Coeffs                         = compuMethods(i).Coeffs.toArray();
                CompuMethodStruct(i).CoeffsLinear                   = compuMethods(i).CoeffsLinear.toArray();
            end    
            
            compuTabs = a2lObj.Module.CompuTabs.toArray();
            for i=1:length(compuTabs) 
                 if isa(compuTabs(i), 'xcp.a2l.CompuVTab')
                    CompuTabStruct(i).Name                    = compuTabs(i).Name;
                    CompuTabStruct(i).ConversionType          = compuTabs(i).ConversionType;
                    CompuTabStruct(i).ClassType               = 'CompuVTab';
                    CompuTabStruct(i).DefaultValue            = compuTabs(i).DefaultValue;
                    CompuTabStruct(i).DefaultValueNumeric     = string();
                    CompuTabStruct(i).InVal                   = compuTabs(i).InTab;
                    CompuTabStruct(i).OutVal                  = compuTabs(i).OutTab;      
            
                elseif isa(compuTabs(i), 'xcp.a2l.CompuVTabRange')
                    CompuTabStruct(i).Name                    = compuTabs(i).Name;
                    CompuTabStruct(i).ConversionType          = 'TAB_VERB';
                    CompuTabStruct(i).ClassType               = 'CompuVTabRange';
                    CompuTabStruct(i).DefaultValue            = compuTabs(i).DefaultValue;
                    CompuTabStruct(i).DefaultValueNumeric     = string();
                    CompuTabStruct(i).InValMin                = compuTabs(i).InValMin;
                    CompuTabStruct(i).InValMax                = compuTabs(i).InValMax;
                    CompuTabStruct(i).OutVal                  = compuTabs(i).OutVal;      
            
                else
                    CompuTabStruct(i).Name                    = compuTabs(i).Name;
                    CompuTabStruct(i).ConversionType          = compuTabs(i).ConversionType;
                    CompuTabStruct(i).ClassType               = 'CompuTab';
                    CompuTabStruct(i).DefaultValueNumeric     = string(compuTabs(i).DefaultValueNumeric);
                    CompuTabStruct(i).InTab                   = compuTabs(i).InTab;
                    CompuTabStruct(i).OutTab                  = compuTabs(i).OutTab;      
                end  
            end   
            
            measurementsInfoJSON        = jsonencode(measurementsStruct);
            compuMethodInfoJSON         = jsonencode(CompuMethodStruct);
            compuTabInfoJSON            = jsonencode(CompuTabStruct);
        end
        
        function uniqueConfigID
            %    XCP.BLKMASKS.CONFIGBASE.UNIQUECONFIGID Determines a unique configuration ID for
            %    each XCP UDP or CAN Configuration block copied over in a model. 

            % Get the current block.
            blk = gcb;
            blkh = gcbh;
            TLTypeStr = xcp.blkmasks.utils.getTLTypeStr(gcbh);

            % @todo update the usage of edit-time filter filterOutInactiveVariantSubsystemChoices()
            % instead use the post-compile filter activeVariants() - g2603543
            allConfigBlocks = find_system(bdroot, ...
                                          'MatchFilter',@Simulink.match.internal.filterOutInactiveVariantSubsystemChoices,... % look only inside active choice of VSS
                                          'FollowLinks', 'on', ...
                                          'IncludeCommented',   'on', ...
                                          'LookUnderMasks', 'on', ...
                                          'MaskType', ['XCP ',TLTypeStr,' Configuration']);
            currentBlockIndex = ismember(allConfigBlocks, blk);

            % Remove the current block from the list.
            allConfigBlocks(currentBlockIndex) = [];

            % Loop through config blocks and find slave ID indices.
            if isempty(allConfigBlocks) % Return, by default set to 1. 
                set_param(blkh, 'SlaveID', '1');
                set_param(blkh, 'SlaveName', [TLTypeStr,'_Config1']);
                return;
            end

            % Continue if we have multiple slaves.
            ids = zeros(1, length(allConfigBlocks));
            for idx = 1: length(allConfigBlocks)
                ids(idx) = str2double(get_param(allConfigBlocks{idx}, 'SlaveID'));
            end

            ids = [0 unique(ids)];
            diffs = diff(ids);
            indices = find(diffs~=1);
            if isempty(indices)
                uniqueSlaveID = length(ids);
            else
                uniqueSlaveID = indices(1);
            end

            % Set it on the current block.
            set_param(blkh, 'SlaveID', num2str(uniqueSlaveID));
            set_param(blkh, 'SlaveName', strcat(TLTypeStr,'_Config', num2str(uniqueSlaveID)));
        end
        
        function validateModel
            % Validate the block in the XCP Model context
            blkh = gcbh; 
            % No seed/key support for Simulink Real-Time
            if ~isempty(regexpi(get_param(bdroot, 'SystemTargetFile'),'^slrealtime', 'once')) ...
                    && strcmp(get_param(gcb,'EnableSecurity'),'on')
                TLTypeStr = xcp.blkmasks.utils.getTLTypeStr(blkh);
                error(message('xcp:xcpblks:noSeedKeySupportSLRT',TLTypeStr, gcb));
            end
        end %validateModel

        
        function structToReturn = getWidgetTags()
            structToReturn.SlaveName = 'configname_tag';
            structToReturn.FileName = 'a2ledit_tag';
            structToReturn.AddFile = 'a2lbrowse_tag';
            structToReturn.EnableSecurity = 'securitybox_tag';
            structToReturn.SeedKeyLib = 'securityedit_tag';
            structToReturn.EnableStatus = 'connstatus_tag';
            structToReturn.SampleTime = 'sampletime_tag';
        end
        
        function validateA2L(blkh, a2lObj)
            TLTypeStr = xcp.blkmasks.utils.getTLTypeStr(blkh);
            tlInfo = [];
            switch TLTypeStr
                case 'CAN'
                    tlInfo = a2lObj.getCANInfo();
                case 'UDP'
                    tlInfo = a2lObj.getUDPInfo();
                case 'CAN FD'
                    tlInfo = a2lObj.getCANInfo();
            end
            
            if isempty(tlInfo)
                error(message('xcp:xcpblks:emptyTLInfo',TLTypeStr));
            end
            
            err = '';
            if isempty(tlInfo.CommonParameters.ProtocolLayer)
                err = [err, getString(message('xcp:xcpblks:emptyPLInfo')), newline];
            end
            if isempty(tlInfo.CommonParameters.DAQ)
                err = [err, getString(message('xcp:xcpblks:emptyDAQInfo')), newline];
            end
            if ~isempty(err)
                error(err);
            end
        end
		
   end
    
end



