classdef helperBluetoothLMPPDUConfig < ble.internal.ConfigBase
%helperBluetoothLMPPDUConfig Create a Bluetooth link manager protocol (LMP)
%protocol data unit (PDU) configuration object
%   CFGLMP = helperBluetoothLMPPDUConfig creates a Bluetooth LMP PDU
%   configuration object, CFGLMP, with default values.
%
%   CFGLMP = helperBluetoothLMPPDUConfig(Name, Value) creates a Bluetooth
%   LMP control PDU configuration object, CFGLMP, with the specified
%   property Name set to the specified Value. You can specify additional
%   name-value pair arguments in any order as (Name1, Value1, ..., NameN,
%   ValueN).
%
%   helperBluetoothLMPPDUConfig properties:
%
%   PDUType                  - Type of LMP PDU
%   TransactionID            - Transaction identifier
%   RequestedOpcode          - Opcode of the requested PDU
%   ErrorCode                - Error code
%   UsedChannels             - List of used data channels
%   AFHInstant               - AFH instant
%   AFHMode                  - AFH mode      
%   AFHChannelClassification - AFH channel classification
%   AFHReportingMode         - AFH reporting mode
%   AFHMinInterval           - AFH minimum interval
%   AFHMaxInterval           - AFH maximum interval
%
%   % Example 1: 
%   % Create a helperBluetoothLMPPDUConfig object for an LMP PDU by 
%   % specifying type of PDU as 'LMP accepted', transaction ID as 1, and
%   % requested opcode as 60.
%
%   cfgLMP                 = helperBluetoothLMPPDUConfig;
%   cfgLMP.PDUType         = 'LMP accepted';
%   cfgLMP.TransactionID   = 1;
%   cfgLMP.RequestedOpcode = 'LMP set AFH';
%
%   % Example 2:
%   % Create a helperBluetoothLMPPDUConfig object for a 'LMP channel
%   % classification request' PDU with AFH minimum interval of 1800 slots
%   % and AFH maximum interval of 24000 slots.
%
%   cfgLMP = helperBluetoothLMPPDUConfig(...
%                          'PDUType', 'LMP channel classification request', ...
%                          'TransactionID', 0, ...
%                          'AFHReportingMode', true, ...
%                          'AFHMinInterval', 1800, ...
%                          'AFHMaxInterval', 24000);
%
%   % Example 3:
%   % Create a helperBluetoothLMPPDUConfig object for a 'LMP set AFH' PDU
%   % with the AFH mode enabled. The master clock value at that instant is
%   % 'c120' and used channels is set as [0 4 12 16 18 24 25].
%
%   cfgLMP               = helperBluetoothLMPPDUConfig('PDUType', 'LMP set AFH');
%   cfgLMP.TransactionID = 0;
%   cfgLMP.AFHInstant   = 'c120';
%   cfgLMP.AFHMode       = true;
%   cfgLMP.UsedChannels  = [0:20 20:40];
%
%   % Example 4:
%   % LMP PDUs end-to-end workflow.
%
%   % Tx side
%   % Create a helperBluetoothLMPPDUConfig object for an LMP PDU of type
%   % 'LMP not accepted'.
%   cfgTx                 = helperBluetoothLMPPDUConfig('PDUType', 'LMP not accepted');
%   cfgTx.TransactionID   = 1;
%   cfgTx.RequestedOpcode = 'LMP set AFH';
%   cfgTx.ErrorCode       = '1E';
%   %  Generate a LMP PDU using 'cfgTx'.
%   pdu = helperBluetoothLMPPDU(cfgTx);
%
%   % Rx side
%   % Decode a LMP PDU.
%   [status, message, cfgRx] = helperBluetoothLMPPDUDecode(pdu);
%   if status == 0 % Decoding is successful
%       cfgRx
%   else
%       fprintf('LMP PDU decoding status :: ');
%       disp(message);
%   end
%
%   See also helperBluetoothLMPPDUDecode, helperBluetoothLMPPDUConfig.

%   Copyright 2020 The MathWorks, Inc.

%#codegen

properties
    %PDUType LMP control PDU type
    %   Specify the PDU type as one of 'LMP accepted' | 'LMP not accepted'
    %   |'LMP set AFH' | 'LMP channel classification' | 'LMP channel
    %   classification request'. This property specifies the type of LMP
    %   PDU. The default value is 'LMP accepted'.
    PDUType = 'LMP accepted'
    
    %TransactionID Transaction identifier
    %   Specify the transaction ID as 0 if the transaction was initiated by
    %   the master and 1 if the transaction was initiated by the slave.
    %   This property specifies the ID of the transaction. The default
    %   value is 1.
    TransactionID = 1
    
    %RequestedOpCode Opcode of the requested PDU
    %   Specify the requested OpCode as 'LMP set AFH'. This property
    %   specifies the type of LMP PDU. The default value is 'LMP set AFH'.
    RequestedOpcode = 'LMP set AFH'
    
    %ErrorCode Error code
    %   Specify the error code as a 2-element character vector or string
    %   scalar denoting a 1-octet hexadecimal value in the range ['01',
    %   '45']. This property specifies the reason for the error in
    %   transaction. The default value is '1F'.
    ErrorCode = '1F'
    
    %AFHInstant AFH Instant
    %   Specify the AFH instant as a 8-element character vector or string
    %   scalar denoting hexadecimal value or a decimal value. This value is
    %   chosen by the master and shall be an even value. This value
    %   indicates the future master clock instant value from where the AFH
    %   hop selection shall be enabled. The default value is 96.
    AFHInstant = 96

    %AFHMode AFH mode
    %   Specify the AFH mode as a scalar logical. A true value indicates
    %   that AFH mode is enabled. The default value is false.
    AFHMode (1, 1) logical = false
    
    %UsedChannels List of used data channels
    %   Specify the assigned data channel indices as an integer vector with
    %   element values in the range [0, 78]. The vector length must be
    %   greater than or equal to 20 i.e. at least twenty channels must be
    %   set as used channels. This property specifies the set of good
    %   channels classified by master. The default value is a row vector
    %   containing all the channel indices [0:78].
    UsedChannels = 0:78
    
    %AFHChannelClassification AFH channel classification
    %   Specify the AFH channel classification as a 40-element integer
    %   vector. This property specifies the classification of channels. The
    %   n'th element defines the classification of channels 2n and 2n+1,
    %   where n in the range [0, 39] other than the last element which just
    %   contains the classification of channel 78. The value of each
    %   element indicates: 0 : unknown 1 : good 2 : reserved for future use
    %   3 : bad The default value is 1 for each element.
    AFHChannelClassification = ones(1, 40)
    
    %AFHReportingMode AFH reporting mode
    %   Specify the AFH reporting mode as a scalar logical. A true value
    %   specifies that AFH reporting is enabled. The default value is
    %   false.
    AFHReportingMode (1, 1) logical = false
    
    %AFHMinInterval AFH minimum interval
    %   Specify the AFH minimum interval as an even integer range [1600,
    %   48000]. This property indicates the minimum number of slots between
    %   two LMP channel classification commands. The default value is 1600.
    AFHMinInterval = 1600
    
    %AFHMaxInterval AFH maximum interval
    %   Specify the AFH maximum interval as an even integer range [1600,
    %   48000]. This property specifies the maximum number of slots between
    %   two LMP channel classification commands. The default value is
    %   48000.
    AFHMaxInterval = 48000
end

properties (Constant, Hidden)
    PDUType_Values = {...
        'LMP accepted'; ...
        'LMP not accepted'; ...
        'LMP set AFH'; ...
        'LMP channel classification'; ...
        'LMP channel classification request'}
end

properties(Hidden)
    %HideDisplay Hide the properties of LMP PDU object
    %   This is a hidden property used by <a
    %   href="matlab:help('helperBluetoothLMPPDUDecode')">helperBluetoothLMPPDUDecode</a>
    %   function to disable the display of all the properties when LMP PDU
    %   decoding fails.
    HideDisplay (1, 1) logical = false
end

methods
    % Constructor
    function obj = helperBluetoothLMPPDUConfig(varargin)
        obj@ble.internal.ConfigBase('PDUType', 'LMP accepted', ...
            varargin{:})
    end
    
    % For auto-completion
    function v = set(obj, prop)
        v = obj.([prop, '_Values']);
    end

    function obj = set.PDUType(obj, value)
        obj.PDUType = validatestring(value, obj.PDUType_Values, ...
            mfilename, 'PDUType');
    end

    function obj = set.TransactionID(obj, value)
        validateattributes(value, {'numeric'}, {'binary', 'scalar'}, ...
            mfilename, 'TransactionID');
        obj.TransactionID = value;
    end

    function obj = set.RequestedOpcode(obj, value)
        obj.RequestedOpcode = validatestring(value, {'LMP set AFH'}, ...
            mfilename, 'RequestedOpcode');
    end

    function obj = set.ErrorCode(obj, value)
        ble.internal.validateHex(value, [1 2], 'ErrorCode');
        if hex2dec(value) < 1 || hex2dec(value) > 69
            fprintf('Specify the ErrorCode value in the range [''01'', ''45''].\n');
            return;
        end
        obj.ErrorCode = char(value);
    end

    function obj = set.AFHInstant(obj, value)
        if ischar(value) || isstring(value)
            ble.internal.validateHex(value, [1 8], 'AFHInstant');
            clockDec = hex2dec(value);
        else
            clockDec = value;
        end
        validateattributes(clockDec, {'numeric'}, {'scalar', 'nonnegative', ...
            'integer', 'even', '>=', 96, '<=', 2^28-2}, mfilename, 'AFHInstant');
        obj.AFHInstant = clockDec;
    end

    function obj = set.UsedChannels(obj, value)
        validateattributes(value, {'numeric'}, {'vector', 'nonnegative', ...
            'integer', '<=', 78}, mfilename, 'UsedChannels');
        value = unique(value);
        % At least twenty channels must be set as used (good) channels as
        % specified in Bluetooth Core Specification v5.0 - Vol 6, Part B,
        % Section 4.5.8.1.
        if numel(value) < 20
            fprintf('The number of used channels must be at least 20.\n');
            return;
        end
        obj.UsedChannels = value;
    end

    function obj = set.AFHChannelClassification(obj, value)
        validateattributes(value, {'numeric'}, {'integer', '>=', 0, ...
            '<=', 3, 'size', [1 40]}, mfilename, ...
            'AFHChannelClassification');
        obj.AFHChannelClassification = value;
    end

    function obj = set.AFHMinInterval(obj, value)
        validateattributes(value, {'numeric'}, {'integer', '>=', 1600, ...
            '<=', 48000, 'even'}, mfilename, 'AFHMinInterval');
        obj.AFHMinInterval = value;
    end

    function obj = set.AFHMaxInterval(obj, value)
        validateattributes(value, {'numeric'}, {'integer', '>=', 1600, ...
            '<=', 48000, 'even'}, mfilename, 'AFHMaxInterval');
        obj.AFHMaxInterval = value;
    end
end 

methods(Access = protected)
    % Controls the conditional display of properties
    function flag = isInactiveProperty(obj, prop)
        % Initialize
        flag = true;
        
        % Disable display
        if obj.HideDisplay
            return;
        end
        
        % Applicable for all PDUs
        if any(strcmpi(prop, {'TransactionID', 'PDUType'}))
            flag = false;
            return;
        end

        % Based on PDU type
        switch(obj.PDUType)
            % Property RequestedOpcode is applicable for 'LMP accepted'.
            case 'LMP accepted'
                flag = ~strcmpi(prop, 'RequestedOpcode');
            
            % Properties RequestedOpCode and ErrorCode are applicable for
            % 'LMP not accepted'.
            case 'LMP not accepted'
                flag = ~any(strcmpi(prop, {'RequestedOpcode', 'ErrorCode'}));
            
            % Properties AFHMode, AFHChannelMap are applicable for 'LMP set
            % AFH'.
            case 'LMP set AFH'
                flag = ~any(strcmpi(prop, {'AFHInstant', 'AFHMode', ...
                    'UsedChannels'}));
        
            % Property AFHChannelClassification is applicable for 'LMP
            % channel classification'.
            case 'LMP channel classification'
                flag = ~strcmpi(prop, 'AFHChannelClassification');

            % Properties AFHReportingMode, AFHMinInterval, AFHMaxInterval
            % are applicable for 'LMP channel classification request'.
            case 'LMP channel classification request'
                flag = ~any(strcmpi(prop, {'AFHReportingMode', ...
                    'AFHMinInterval', 'AFHMaxInterval'}));
        end
    end
end
end

% LocalWords:  Bluetooth LMP PDU CFGLMP Opcode AFH opcode pdu LMPPDU n'th
