classdef GeneralFrameConfig < matlab.mixin.CustomDisplay
  %GENERALFRAMECONFIG Configuration for general, library-wide command frames
  % FRAMECONFIG = GENERALFRAMECONFIG creates a configuration object for
  % general ZigBee command frames that are not specific to an individual cluster.
  %
  % FRAMECONFIG = GENERALFRAMECONFIG(Name, Value) creates a configuration
  % object for general, library-wide ZigBee command frames  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).
  %
  % GeneralFrameConfig properties:
  %
  % CommandType       - Command type
  % CommandToRespond  - Type of command to respond
  % AttributeID       - Attribute ID
  % Status            - Attribute status
  % AttributeType     - Attribute type
  % AttributeValue    - Attribute value
  %
  % See also zigbee.GeneralFrameGenerator, zigbee.GeneralFrameDecoder.
  
  % Copyright 2017-2023 The MathWorks, Inc.

properties
  %CommandType Command type
  % Specify CommandType as one of 'Read Attributes' | 'Read Attributes
  % Response' | 'Write Attributes' | 'Write Attributes Undivided' | 'Write
  % Attributes Response' | 'Write Attributes No Response' | 'Report
  % Attributes' | 'Default Response'. The default is 'Read Attributes'.
  CommandType = 'Read Attributes';
    
  %AttributeID Attribute ID
  % Specify AttributeID as a four-character hexadecimal vector expressing a
  % 16-bit ID. The default is '0000'.
  AttributeID = '0000';
  
  %Status Attribute status
  % Specify Status as one of 'Success' | 'Failure' |  'Not authorized' |
  % 'Non-zero reserved' | 'Malformed command' | 'Unsupported cluster
  % command' | 'Unsupported general command' | 'Unsupported manufacturer
  % cluster command' | 'Unsupported manufacturer general command' |
  % 'Invalid field' | 'Unsupported attribute' |  'Invalid value' | 'Read
  % only' | 'Insufficient space' | 'Duplicate exists' | 'Not found' |
  % 'Unreportable attribute' | 'Invalid data type' | 'Invalid selector' |
  % 'Write only' | 'Inconsistent startup state' | 'Defined out of band' |
  % 'Inconsistent' | 'Action denied' | 'Timeout' | 'Abort' | 'Invalid
  % image' | 'Wait for data' | 'No image available' | 'Require more image'
  % | 'Notification pending' | 'Hardware failure' | 'Software failure' |
  % 'Calibration error' | 'Unsupported cluster'. The default is 'Success'.
  Status = 'Success';
  
  %AttributeType Attribute type
  % Specify AttributeType as one of '8-bit data' | '16-bit data' | '24-bit
  % data' |'32-bit data' | '40-bit data' | '48-bit data' | '56-bit data' |
  % '64-bit data', | 'Boolean' | '8-bit bitmap' | '16-bit bitmap' | '24-bit
  % bitmap' | '32-bit bitmap' | '40-bit bitmap' | '48-bit bitmap' | '56-bit
  % bitmap' | '64-bit bitmap' | 'uint8' | 'uint16' | 'uint24' | 'uint32' |
  % 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'int8' | 'int16' | 'int24'
  % | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | '8-bit enumeration'
  % | '16-bit enumeration' | 'single' | 'double'. The default is 'double'.
  AttributeType = 'double';
  
	%AttributeValue Attribute value
  % Specify AttributeValue according to the AttributeType data type. The
  % default is 0.
  AttributeValue = 0;
  
	%CommandToRespond Type of command to respond
  % Specify CommandToRespond a two-character hexadecimal vector expressing
  % an 8-bit ID. The default is '00'.
  CommandToRespond = '00';
end

properties(Constant, Hidden)
  CommandTypeValues = {'Read Attributes', 'Read Attributes Response', 'Write Attributes', ...
      'Write Attributes Undivided', 'Write Attributes Response', 'Write Attributes No Response', ...
      'Report Attributes', 'Default Response'};
  StatusValues = {'Success', 'Failure', 'Not authorized', 'Non-zero reserved', ...
    'Malformed command', 'Unsupported cluster command', 'Unsupported general command', ...
    'Unsupported manufacturer cluster command', 'Unsupported manufacturer general command', ...
    'Invalid field', 'Unsupported attribute', 'Invalid value', 'Read only', ...
    'Insufficient space', 'Duplicate exists', 'Not found', 'Unreportable attribute', ...
    'Invalid data type', 'Invalid selector', 'Write only', 'Inconsistent startup state', ...
    'Defined out of band', 'Inconsistent', 'Action denied', 'Timeout', 'Abort', ...
    'Invalid image', 'Wait for data', 'No image available', 'Require more image', ...
    'Notification pending', 'Hardware failure', 'Software failure', 'Calibration error', ...
    'Unsupported cluster'};
  AttributeTypeValues = {'8-bit data', '16-bit data', '24-bit data', '32-bit data', ...
               '40-bit data', '48-bit data', '56-bit data', '64-bit data', ...
               'Boolean', '8-bit bitmap', '16-bit bitmap', '24-bit bitmap', ...
               '32-bit bitmap', '40-bit bitmap', '48-bit bitmap', '56-bit bitmap', ...
               '64-bit bitmap', 'uint8', 'uint16', 'uint24', 'uint32', 'uint40', ...
               'uint48', 'uint56', 'uint64', 'int8', 'int16', 'int24', ...
               'int32', 'int40', 'int48', 'int56', 'int64', '8-bit enumeration', ...
               '16-bit enumeration', 'single', 'double'};
  DaysOfWeek = {'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'};
  Months = {'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', ...
            'September', 'October', 'November', 'December'};
end

methods
  function obj = GeneralFrameConfig(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.CommandType(obj, value)
    obj.CommandType = validatestring(value, obj.CommandTypeValues, '', 'CommandType');
  end
  
  function obj = set.CommandToRespond(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'2d', 'ncols', 2}, '', 'CommandToRespond');
    obj.CommandToRespond = value;
  end

  function obj = set.AttributeID(obj, value)
    value = convertStringsToChars(value);
    validateattributes(value, {'char'}, {'2d', 'ncols', 4}, '', 'AttributeID');
    obj.AttributeID = value;
  end

  function obj = set.Status(obj, value)
    obj.Status = validatestring(value, obj.StatusValues, '', 'Status');
  end
    
  function obj = set.AttributeType(obj, value)
    obj.AttributeType = validatestring(value, obj.AttributeTypeValues, '', 'AttributeType');
  end
  
  function obj = set.AttributeValue(obj, value)
    switch obj.AttributeType %#ok<MCSUP>
      case {'8-bit data', 'uint8', '8-bit bitmap', '8-bit enumeration'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', 2^8}, '', 'AttributeValue');

      case {'16-bit data', 'uint16', '16-bit bitmap', '16-bit enumeration'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', 2^16}, '', 'AttributeValue');

      case {'24-bit data', 'uint24', '24-bit bitmap'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', 2^24}, '', 'AttributeValue');

      case {'32-bit data', 'uint32', '32-bit bitmap', 'UTCTime', 'BACnet OID'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', (2^32)}, '', 'AttributeValue');

      case {'40-bit data', 'uint40', '40-bit bitmap'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', (2^40)}, '', 'AttributeValue');

      case {'48-bit data', 'uint48', '48-bit bitmap'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', (2^48)}, '', 'AttributeValue');

      case {'56-bit data', 'uint56', '56-bit bitmap'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', (2^56)}, '', 'AttributeValue');

      case {'64-bit data', 'uint64', '64-bit bitmap'}
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'nonnegative', 'real', '<', (2^64)}, '', 'AttributeValue');

      case 'Boolean'
        validateattributes(value, {'logical'}, {'scalar'}, 'validateDataValues', 'Boolean');

      case 'int8'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^7)-1), '>=', -(2^7)}, '', 'AttributeValue');

      case 'int16'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^15)-1), '>=', -(2^15)}, '', 'AttributeValue');

      case 'int24'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^23)-1), '>=', -(2^23)}, '', 'AttributeValue');

      case 'int32'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^31)-1), '>=', -(2^31)}, '', 'AttributeValue');

      case 'int40'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^39)-1), '>=', -(2^39)}, '', 'AttributeValue');

      case 'int48'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^47)-1), '>=', -(2^47)}, '', 'AttributeValue');

      case 'int56'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^55)-1), '>=', -(2^55)}, '', 'AttributeValue');

      case 'int64'
        validateattributes(value, {'numeric'}, {'scalar', 'integer', 'real', '<=', ((2^63)-1), '>=', -(2^63)}, '', 'AttributeValue');

      case {'Octet string', 'Character string', 'Long octet string', 'Long character string'}
        value = convertStringsToChars(value);  
        validateattributes(value, {'char'}, {'2d'}, '', 'AttributeValue');

      case {'Cluster ID', 'Attribute ID'}
        value = convertStringsToChars(value);
        validateattributes(value, {'char'}, {'row', 'numel', 4}, '', 'AttributeValue');

      case {'IEEE address', '128-bit security key'}
        value = convertStringsToChars(value);
        validateattributes(value, {'char'}, {'row'}, '', 'AttributeValue');

      case {'Array', 'struct', 'Set', 'Bag'}
        validateattributes(value, {'struct'}, {'row'}, '', 'AttributeValue');

      case 'Time of day'
        hours = value(1:2);
        mins = value(4:5);
        secs = value(7:8);
        hundthsec = value(10:11);
        validateattributes(str2double(hours), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 23, '>=', 0}, 'validateDataValues', 'TimeOfDay.Hours');
        validateattributes(str2double(mins), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 59, '>=', 0}, 'validateDataValues', 'TimeOfDay.Minutes');
        validateattributes(str2double(secs), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 59, '>=', 0}, 'validateDataValues', 'TimeOfDay.Seconds');
        validateattributes(str2double(hundthsec), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 99, '>=', 0}, 'validateDataValues', 'TimeOfDay.HundredthsSeconds');
        validatestring(value(3), {':'}, 'validateDataValues', 'TimeOfDay.Colon');
        validatestring(value(6), {':'}, 'validateDataValues', 'TimeOfDay.Colon');
        validatestring(value(9), {'.'}, 'validateDataValues', 'TimeOfDay.Dot');

      case 'Date'
        dateStr = strsplit(value, ',');
        day = dateStr{1};
        date = strsplit(dateStr{2}, '-');
        validatestring(day, obj.DaysOfWeek, 'validateDataValues', 'Date.DayOfWeek');
        validatestring(date{2}, obj.Months, 'validateDataValues', 'Date.DayOfWeek');
        validateattributes(str2double(date{1}), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 31, '>=', 1}, 'validateDataValues', 'Date.DayOfMonth');
        validateattributes(str2double(date{3}), {'numeric'}, {'scalar', 'integer', 'nonnan', 'real', '<=', 2155, '>=', 1900}, 'validateDataValues', 'Date.Year');

      case 'single'
        validateattributes(value, {'single'}, {'scalar', 'nonnan', 'real'}, 'validateDataValues', 'single');

      case 'double'
        validateattributes(value, {'double'}, {'scalar', 'nonnan', 'real'}, 'validateDataValues', 'double');
    end
    obj.AttributeValue = value;
  end  


end

methods (Access = protected)
  function groups = getPropertyGroups(obj)
    propList = properties(obj);
    activeIdx1 = true(size(propList));

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

  function flag = isInactiveProperty(obj, prop)
    flag = false;
    if strcmp(prop, 'AttributeID')
      flag = strcmp(obj.CommandType, 'Default Response');
      
      if strcmp(obj.CommandType, 'Write Attributes Response')
        flag = flag | strcmp(obj.Status, 'Success');
      end
      
    elseif any(strcmp(prop, {'AttributeType', 'AttributeValue'}))
      flag = ~any(strcmp(obj.CommandType, {'Read Attributes Response', 'Write Attributes', 'Write Attributes Undivided', 'Write Attributes No Response', 'Report Attributes'}));
      
      if strcmp(obj.CommandType, 'Read Attributes Response')
        flag = flag | ~strcmp(obj.Status, 'Success');
      end
      
    elseif strcmp(prop, 'CommandToRespond')
      flag = ~strcmp(obj.CommandType, 'Default Response');
      
    elseif strcmp(prop, 'Status')
      flag = ~any(strcmp(obj.CommandType, {'Read Attributes Response', ...
                      'Write Attributes Response', 'Default Response'}));
        
    end
  end
end
end
