classdef APSFrameConfig < zigbee.internal.SecurityConfig
%APSFrameConfig Configuration for ZigBee APP-layer frames
%   FRAMECONFIG = APSFrameConfig creates a configuration object for ZigBee
%   APP frames.
%
%   FRAMECONFIG = APSFrameConfig(Name,Value) creates a ZigBee APP frame
%   configuration object 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).
%
%   APSFrameConfig properties:
%
%   FrameType               - The type of the application-layer frame
%   CommandType             - The type of the application-layer command
%   DeliveryMode            - Delivery mode (unicast vs broadcast etc.)
%   AcknowledgmentRequest   - Option to request Acknowledgment
%   AcknowledgmentType      - The type of the acknowledged frame
%   ExtendedHeader          - Presence of an extended header
%   DestinationEndpoint     - Final recipient of frame
%   GroupAddress            - Group address
%   ClusterID               - Cluster identifier
%   ProfileID               - Profile identifier
%   SourceEndpoint          - Initial originator of frame
%   APSCounter              - Frame counter for the application layer
%   FragmentationStatus     - Status of fragmentation
%   BlockNumber             - Block number
%   ACKBitField             - Acknowledgment bitfield
%   Security                - Indication of secure frame
%   DataEncryption          - Indication of encrypted payload
%   MICLength               - Length of message integrity code (MIC)
%   KeyIdentifier           - Key type
%   ExtendedNonce           - Presence of sender address in auxiliary (security) header
%   FrameCounter            - Frame counter
%   SecuritySourceAddress   - Source address in auxiliary (security) header
%   KeySequence             - Key sequence number
%
%   See also zigbee.APSFrameGenerator, zigbee.APSFrameDecoder.

%   Copyright 2016-2023 The MathWorks, Inc.

properties
  %FrameType The type of the application-layer (APP) frame
  % Specify the type of the application-layer (APP) frame as one of 'Data' |
  % 'Acknowledgment' | 'Command'. The default is 'Data'.
  FrameType = 'Data'
  
  %CommandType The name of the application-layer command
  % Specify the name of the application-layer command as one of 'Key establishment' |
  % 'Transport key' | 'Update device' | 'Remove device' | 'Request key' |
  % 'Switch key' | 'Entity authentication' | 'Tunnel'. The default is 'Key establishment'.
  CommandType = 'Key establishment'
  
  %DeliveryMode Delivery mode
  % Specify the delivery mode as one of 'Unicast' | 'Broadcast' | 'Group' |
  % 'Indirect'. The default is 'Unicast'.
  DeliveryMode = 'Unicast'
  
  %AcknowledgmentRequest Option to request Acknowledgment
  % Specify AcknowledgmentRequest as a logical scalar. If true, then the
  % frame requires to be acknowledges with an Acknowledgment frame. The
  % default is false.
  AcknowledgmentRequest = false
  
  %AcknowledgmentType The type of the acknowledged frame
  % Specify the type of the acknowledged frame as one of 'Data' |
  % 'Command'. This property applies only when FrameType is set to
  % 'Acknowledgment'. The default is 'Data'.
  AcknowledgmentType = 'Data'
  
  %ExtendedHeader Indication of an extended header
  % Specify ExtendedHeader as a logical scalar to indicate whether an
  % extended header is present or not. The default is false.
  ExtendedHeader = false
  
  %DestinationEndpoint Final recipient of frame
  % Specify DestinationEndpoint as a 2-character hexadecimal vector
  % expressing the 8-bit address of the final recipient of the frame. This
  % property can apply only when the DeliveryMode is one of 'Unicast' |
  % 'Broadcast' | 'Indirect'. The default is '00'.
  DestinationEndpoint = '00'
  
  %GroupAddress Group address
  % Specify GroupAddress as a 4-character hexadecimal vector expressing the
  % 16-bit address of the destination group. This property applies only
  % when the DeliveryMode is 'Group'. The frame will be delivered to all
  % endpoints belonging to the group addressed by GroupAddress. The
  % default is '0000'.
  GroupAddress = '0000'
  
  %ClusterID Cluster identifier
  % Specify ClusterID as a 4-character hexadecimal vector expressing the
  % 16-bit cluster ID. This property applies only for data frames and for
  % Acknowledgments of data frames. The default is '0000'.
  ClusterID = '0000'
  
  %ProfileID Profile identifier
  % Specify ProfileID as a 4-character hexadecimal vector expressing the
  % 16-bit profile ID. The profile ID can be used for filtering messages.
  % This property applies only for data frames and for Acknowledgments of
  % data frames. The default is '0000'.
  ProfileID = '0000'
  
  %SourceEndpoint Initial originator of frame
  % Specify SourceEndpoint as a 2-character hexadecimal vector expressing
  % the 8-bit address of the initial originator of the frame. This property
  % applies only for data frames and for Acknowledgments of data frames.
  % The default is '00'.
  SourceEndpoint = '00'
  
  %APSCounter Frame counter for the application layer
  % Specify APSCounter as a scalar non-negative real integer. This field
  % can prevent reception of duplicate frames and should be incremented for
  % each new transmission. The default is 0.
  APSCounter = 0
  
  %FragmentationStatus Status of fragmentation
  % Specify FragmentationStatus as one of 'No fragmentation' | 'First
  % fragment' | 'Subsequent fragment' to indicate whether transmission is
  % fragmented and whether this frame is the first fragment or not. The
  % default is 'No fragmentation'.
  FragmentationStatus = 'No fragmentation'
  
  %BlockNumber Block number
  % Specify BlockNumber as a scalar non-negative real integer. This
  % property applies only when FragmentationStatus is 'First
  % fragment' or 'Subsequent fragment'. In the first case, BlockNumber
  % specifies the total number of fragments. In the second case, it
  % specifies the current frame's fragment number. The default is 0.
  BlockNumber = 0
  
  %ACKBitField Acknowledgment bitfield
  % Specify ACKBitField as a 2-character hexadecimal vector indicating the
  % 8-bit Acknowledgment bit field, i.e., which blocks of a fragmented
  % transmission have been successfully received. This property applies
  % only when FrameType is set to 'Acknowledgment' and FragmentationStatus
  % is set to 'First fragment' or 'Subsequent fragment'. The default is
  % '00'.
  ACKBitField = '00'
end

properties(Constant, Hidden)
  FrameTypeValues               = {'Data', 'Command', 'Acknowledgment'}
  AcknowledgmentTypeValues     = {'Data', 'Command'}
  CommandTypeValues = {'Key establishment', 'Transport key', 'Update device', 'Remove device', ...
    'Request key', 'Switch key', 'Entity authentication',  'Tunnel'}
  DeliveryModeValues            = {'Unicast', 'Broadcast', 'Group', 'Indirect'}
  FragmentationStatusValues     = {'No fragmentation', 'First fragment', 'Subsequent fragment'}
end

methods
  function obj = APSFrameConfig(varargin)
       
    % Apply constructor name value pairs:
    for i = 1:2:nargin
        obj.(varargin{i}) = varargin{i+1};
    end
  end

  % For auto-completion:
  function v = set(obj, prop)
    v = obj.([prop, 'Values']);
  end

  function obj = set.FrameType(obj, value)
    obj.FrameType = validatestring(value, obj.FrameTypeValues, '', 'FrameType');
  end
  
  function obj = set.CommandType(obj, value)
    obj.CommandType = validatestring(value, obj.CommandTypeValues, '', 'CommandType');
  end

  function obj = set.AcknowledgmentType(obj, value)
    obj.AcknowledgmentType = validatestring(value, obj.AcknowledgmentTypeValues, '', 'AcknowledgmentType');
  end
  
  function obj = set.DeliveryMode(obj, value)
    obj.DeliveryMode = validatestring(value, obj.DeliveryModeValues, '', 'DeliveryMode');
  end
  
  function obj = set.FragmentationStatus(obj, value)
    obj.FragmentationStatus = validatestring(value, obj.FragmentationStatusValues, '', 'FragmentationStatus');
  end
 
  function obj = set.AcknowledgmentRequest(obj, value)
    validateattributes(value, {'logical'}, {'scalar'}, '', 'AcknowledgmentRequest');
    obj.AcknowledgmentRequest = value;
  end
  
  function obj = set.ExtendedHeader(obj, value)
    validateattributes(value, {'logical'}, {'scalar'}, '', 'ExtendedHeader');
    obj.ExtendedHeader = value;
  end
  
  function obj = set.DestinationEndpoint(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'row', 'numel', 2}, '', 'DestinationEndpoint');
    obj.DestinationEndpoint = value;
  end
  
  function obj = set.SourceEndpoint(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'row', 'numel', 2}, '', 'SourceEndpoint');
    obj.SourceEndpoint = value;
  end
  
  function obj = set.ACKBitField(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'row', 'numel', 2}, '', 'ACKBitField');
    obj.ACKBitField = value;
  end
  
  function obj = set.GroupAddress(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'row', 'numel', 4}, '', 'GroupAddress');
    obj.GroupAddress = value;
  end
  
  function obj = set.ClusterID(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'row', 'numel', 4}, '', 'ClusterID');
    obj.ClusterID = value;
  end
  
  function obj = set.ProfileID(obj, value)
    value = convertStringsToChars(value);  
    validateattributes(value, {'char'}, {'row', 'numel', 4}, '', 'ProfileID');
    obj.ProfileID = value;
  end
  
  function obj = set.APSCounter(obj, value)
    validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real'}, '', 'APSCounter');
    obj.APSCounter = value;
  end
  
  function obj = set.BlockNumber(obj, value)
    validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real'}, '', 'BlockNumber');
    obj.BlockNumber = value;
  end
  
end

methods (Access=protected)

  function groups = getPropertyGroups(obj)
    
    propList1  = {'FrameType'; 'CommandType'; 'APSCounter'; ...
                  'AcknowledgmentRequest'; 'AcknowledgmentType'};
    activeIdx1 = true(size(propList1));

    for n = 1:numel(propList1)
        if isInactiveProperty(obj,propList1{n})
            activeIdx1(n) = false;
        end
    end
    groups = coder.nullcopy(repmat(matlab.mixin.util.PropertyGroup(propList1(activeIdx1)), 1, 4));
    groups(1) = matlab.mixin.util.PropertyGroup(propList1(activeIdx1));

    propList2  = {'DeliveryMode'; 'DestinationEndpoint'; 'GroupAddress'; 'ClusterID';
                  'ProfileID'; 'SourceEndpoint';};
    title2 = getString(message('lrwpan:ZigBee:AddressingTitle'));
    activeIdx2 = true(size(propList2));

    for n = 1:numel(propList2)
        if isInactiveProperty(obj,propList2{n})
            activeIdx2(n) = false;
        end
    end

    groups(2) = matlab.mixin.util.PropertyGroup(propList2(activeIdx2), title2);

    propList3  = {'ExtendedHeader'; 'FragmentationStatus'; 'BlockNumber'; 'ACKBitField'};
    title3 = getString(message('lrwpan:ZigBee:ExtendedHeaderTitle'));
    activeIdx3 = true(size(propList3));

    for n = 1:numel(propList3)
      if isInactiveProperty(obj,propList3{n})
          activeIdx3(n) = false;
      end
    end

    groups(3) = matlab.mixin.util.PropertyGroup(propList3(activeIdx3), title3);
      
    groups(4) = getPropertyGroups@zigbee.internal.SecurityConfig(obj);
  end

  function flag = isInactiveProperty(obj, prop)
    % Controls the conditional display of properties

    % search if property is one of the inactive base properties:
    flag = isInactiveProperty@zigbee.internal.SecurityConfig(obj, prop);

    if strcmp(prop, 'CommandType')
      flag = ~strcmp(obj.FrameType, 'Command');
    
    elseif strcmp(prop, 'DestinationEndpoint')
      flag = strcmp(obj.DeliveryMode, 'Group') || ...
        (strcmp(obj.FrameType, 'Acknowledgment') && strcmp(obj.AcknowledgmentType, 'Command' ));
    
    elseif strcmp(prop, 'GroupAddress')
      flag = ~strcmp(obj.DeliveryMode, 'Group');
    
    elseif strcmp(prop, 'AcknowledgmentRequest')
      flag = strcmp(obj.FrameType, 'Acknowledgment') ||  ...
             any(strcmp(obj.DeliveryMode, {'Group', 'Broadcast' } ));
    
    elseif strcmp(prop, 'AcknowledgmentType')
      flag = ~strcmp(obj.FrameType, 'Acknowledgment');
    
    elseif any(strcmp(prop, {'ClusterID', 'ProfileID', 'SourceEndpoint'}))
      flag = strcmp(obj.FrameType, 'Command') || ...
        (strcmp(obj.FrameType, 'Acknowledgment') && strcmp(obj.AcknowledgmentType, 'Command' ));
    
    elseif strcmp(prop, 'FragmentationStatus')
      flag = ~obj.ExtendedHeader;
    
    elseif strcmp(prop, 'BlockNumber')
      flag = ~obj.ExtendedHeader || strcmp(obj.FragmentationStatus, 'No fragmentation');
    
    elseif strcmp(prop, 'ACKBitField')
      flag = ~obj.ExtendedHeader || strcmp(obj.FragmentationStatus, 'No fragmentation') || ...
             ~strcmp(obj.FrameType, 'Acknowledgment');
    end
  end
end
  
end

