classdef SerialConfiguration < matlab.System
    % SerialConfigObj = matlabshared.seriallib.internal.system.SerialConfiguration
    % creates a Serial Configuration system object.
    
    % Copyright 2020-2023 The MathWorks, Inc.
    
    % Public, non-tunable properties
    properties(Nontunable)
        % Communication port
        PortSel = '<Select a port...>'
        % Baud rate
        BaudRate = 9600;
        % Data bits
        DataBits = '8';
        % Parity
        Parity = 'none'
        % Stop bits
        StopBits = 1;
        % Byte order
        ByteOrder = 'little-endian';
        % Flow control
        FlowControl = 'none';
        % Timeout
        Timeout = 10;
        ComPort = '<Select a port...>';
    end
    
    %#codegen
    properties (Access = 'private')
        SerialObj
    end
    
    properties (Hidden)
        DataBitsSet = matlab.system.StringSet({'5', '6', '7', '8'});
        ParitySet = matlab.system.StringSet({'none', 'even', 'odd'});
        ByteOrderSet = matlab.system.StringSet({'big-endian', 'little-endian'});
        FlowControlSet = matlab.system.StringSet({'none', 'hardware'});
    end
    
    methods
        %% Constructor
        function obj = SerialConfiguration(varargin)
            % Support name-value pair arguments when constructing object
            setProperties(obj, nargin, varargin{:})
        end
        
        %% Set functions to validate and set the property value.
        function set.Timeout(obj, value)
            validateattributes(value, {'numeric'}, ...
                {'real', 'nonempty', 'nonnan', 'positive', 'scalar'}, ...
                '', 'Timeout');
            obj.Timeout = value;
        end
        
        function set.BaudRate(obj, value)
            validateattributes(value,{'numeric'}, ...
                { 'scalar', 'nonnegative', 'real', 'nonnan', 'integer'}, ...
                '', 'Baud rate')
            obj.BaudRate = value;
        end
        
        function set.StopBits(obj, value)
            if ismember(real(str2double(obj.DataBits)), [6, 7, 8])
                if (~any(ismember(value,[1, 2])) || ~isscalar(value))
                    coder.internal.error('instrument:instrumentblks:invalidStopbitsDatabits1');
                end
            else
                if ismember(real(str2double(obj.DataBits)), 5)
                    if (~any(ismember(value,[1, 1.5])) || ~isscalar(value))
                        coder.internal.error('instrument:instrumentblks:invalidStopbitsDatabits2');
                    end
                end
            end
            obj.StopBits = value;
        end
        
    end
    
    methods(Access = protected)
        %% Define number of inputs and outputs to the system.
        function num = getNumOutputsImpl(~)
            % Define total number of outputs for system.
            num = 0;
        end
        
        function num = getNumInputsImpl(~)
            % Define total number of inputs for system.
            num = 0;
        end
        
        %% Algorithm implementation
        function setupImpl(obj)
            % Configure selected COM Port.
            if strcmpi(obj.PortSel, '<Select a port...>')
                coder.internal.error('instrument:instrumentblks:noPortsSelected');
            end

            % Check if the parameter value is a variable. If yes, resolve
            % the variable and use its value. If parameter value is an
            % existing serial port, do not resolve.
            portValue = obj.PortSel;
            % Use coder.extrinsic to call the function in MATLAB rather than generating
            %code for it. This is done for functions that doesnt support codegen.
            coder.extrinsic('serialportlist');
            coder.extrinsic('slResolve');
            coder.extrinsic('gcb');
            coder.extrinsic('ismember');
            % Use coder.const to evaluate function call at compile time instead of run time.
            blockPath = coder.const(gcb);
            slList = coder.const(serialportlist);
            portInList = coder.const(~ismember(obj.PortSel, slList));
            if portInList
                [portValue, portStatus]= coder.const(@slResolve,obj.PortSel, blockPath);
                if ~portStatus
                    portValue = obj.PortSel;
                end
            end

            obj.SerialObj = matlabshared.seriallib.internal.Serial(portValue, ...
                'IsSharingPort', true, ...
                'IsSharingExistingTimeout', false, ...
                'IsWriteOnly', true, ...
                'BaudRate', obj.BaudRate, ...
                'DataBits', real(str2double(obj.DataBits)), ...
                'Parity', obj.Parity, ...
                'StopBits',  obj.StopBits, ...
                'ByteOrder', obj.ByteOrder, ...
                'FlowControl', obj.FlowControl, ...
                'Timeout', obj.Timeout);
            obj.SerialObj.connect;
            
            if ~obj.SerialObj.InitAccess
                coder.internal.error('instrument:instrumentblks:errorConfiguringPort');
            end
        end
        
        function releaseImpl(obj)
            % Release resources
            disconnect(obj.SerialObj)
        end
    end
end
