classdef helperBLEMeshTransportLayer < handle
%helperBLEMeshTransportLayer Create an object for Bluetooth mesh transport
%layer
%   MESHTRANS = helperBLEMeshTransportLayer creates a Bluetooth mesh
%   transport layer object, with properties and methods related to
%   transport layer.
%   
%   MESHTRANS = helperBLEMeshTransportLayer(Name, Value) creates a
%   Bluetooth mesh transport layer 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).
%
%   helperBLEMeshTransportLayer properties:
%
%   Feature                 - Type of the Bluetooth mesh node
%   FriendAddress           - Unicast address of the friend
%   ElementAddress          - Unicast address of the node
%   SubscriptionAddresses   - List of subscription addresses
%   Friend                  - Friend feature functionality
%   LowPower                - Low Power feature functionality

%   Copyright 2019 The MathWorks, Inc.

properties
    %Feature Type of the Bluetooth mesh node
    %   Specify the feature as one of 'Friend' | 'Low power' | 'None'. This
    %   property indicates the type of the Bluetooth mesh node. The default
    %   value is 'None'.
    Feature = 'None'
    
    %FriendAddress Unicast address of the friend
    %   Specify the friend address as a four-element character vector or
    %   string scalar denoting a two-octet hexadecimal value. For a node
    %   with either Low Power or Friend feature enabled, this property
    %   indicates the unicast address of the friend of this node. The
    %   default value is '0000'.
    FriendAddress = '0000'
    
    %ElementAddress Unicast address of the node
    %   Specify the element address as a four-element character vector or
    %   string scalar denoting a two-octet hexadecimal value. For a node
    %   with Low Power or Friend feature enabled, this property indicates
    %   the unicast address of the element in this node. The default value
    %   is '0000'.
    ElementAddress = '0000'
    
    %SubscriptionAddresses List of subscription addresses
    %   Specify the subscription addresses as a four-element character
    %   vector or string scalar denoting a two-octet hexadecimal value or a
    %   cell array containing list of group addresses. This property
    %   indicates the list of subscription addresses of this node. The
    %   default value is 'FFFF' denoting the fixed group address for all
    %   the nodes in the Bluetooth mesh network.
    SubscriptionAddresses = 'FFFF'
    
    %Friend Friend feature functionality
    %   Specify this property as an object of type <a
    %   href="matlab:help('helperBLEMeshFriendNode')">helperBLEMeshFriendNode</a>.
    %   Friend feature fields are populated using settings of this object.
    %   The default value is an object of type 'helperBLEMeshFriendNode'
    %   with all properties set to their default values. Use this property
    %   only when <a
    %   href="matlab:help('helperBLEMeshTransportLayer.Feature')">Feature</a>
    %   is set to 'Friend'.
    Friend
    
    %LowPower Low Power feature functionality
    %   Specify this property as an object of type <a
    %   href="matlab:help('helperBLEMeshLowPowerNode')">helperBLEMeshLowPowerNode</a>.
    %   Low Power feature fields are populated using settings of this
    %   object. The default value is an object of type
    %   'helperBLEMeshLowPowerNode' with all properties set to their
    %   default values. Use this property only when <a
    %   href="matlab:help('helperBLEMeshTransportLayer.Feature')">Feature</a>
    %   is set to 'Low power'.
    LowPower
end

properties(Constant, Hidden)
    %FeatureValues Optional strings for the property Feature
    FeatureValues = {'Friend', 'Low power', 'None'}
end

properties (Access = private)
    %pTransportQueue Queue for storing transport layer messages except
    %messages destined to Low Power node
    pTransportQueue
end

properties (Hidden, SetAccess = private)
    %pDecodedAccessMessage Decoded access message
    pDecodedAccessMessage
    
    %pAccessMessageReceived Flag for access message reception
    pAccessMessageReceived = false
    
    %pDecodedSRC Decoded source address value
    pDecodedSRC = '0000'
    
    %pDecodedDST Decoded destination address value
    pDecodedDST = '0000'
end

properties (SetAccess = private)
    %TransmittedMsgs Messages transmitted from the transport layer
    TransmittedMsgs = 0
    
    %BytesTransmitted Bytes transmitted from the transport layer
    BytesTransmitted = 0
    
    %ReceivedMsgs Messages received by the transport layer
    ReceivedMsgs = 0
    
    %ReceivedLPNMsgs Friend poll messages received by the Friend node from
    %the Low Power node
    ReceivedLPNMsgs = 0
    
    %BytesReceived Bytes received by the transport layer
    BytesReceived = 0
end

methods
    function obj = helperBLEMeshTransportLayer(varargin)
        % Name-value pairs
        for idx = 1:2:nargin
            obj.(char(varargin{idx})) = varargin{idx+1};
        end
        % Initialize variable sized property for codegen support
        obj.pDecodedAccessMessage = zeros(1, 0);
        % Create a Bluetooth mesh transport queue object with default
        % value
        obj.pTransportQueue = helperBLEMeshQueue(10);
        % Create a Bluetooth mesh Friend node object with default values
        obj.Friend = helperBLEMeshFriendNode();
        % Create a Bluetooth mesh Low Power node object with default values
        obj.LowPower = helperBLEMeshLowPowerNode();
    end
    
    % For auto-completion
    function v = set(obj, prop)
        v = obj.([prop, 'Values']);
    end
    
    function set.pDecodedAccessMessage(obj, value)
        obj.pDecodedAccessMessage = zeros(11,1);
        obj.pDecodedAccessMessage = value;
    end
    
    % Set feature
    function set.Feature(obj, value)
        value = validatestring(value, obj.FeatureValues, mfilename, ...
            'Feature');
        obj.Feature = value;
    end
    
    % Set friend address
    function set.FriendAddress(obj, value)
        ble.internal.validateHex(value, 4, 'FriendAddress')
        obj.FriendAddress = value;
    end
    
    % Set element address
    function set.ElementAddress(obj, value)
        ble.internal.validateHex(value, 4, 'ElementAddress')
        obj.ElementAddress = value;
    end
    
    % Set subscription addresses
    function set.SubscriptionAddresses(obj, value)
        if iscell(value)
            for idx = 1:numel(value)
                ble.internal.validateHex(value{idx}, 4, 'SubscribedAddress');
            end
        else
            ble.internal.validateHex(value, 4, 'SubscribedAddress');
        end
        obj.SubscriptionAddresses = value;
    end
    
    % Set Friend feature
    function set.Friend(obj, value)
        validateattributes(value, {'helperBLEMeshFriendNode'}, {'scalar'}, ...
            mfilename, 'Friend');
        obj.Friend = value;
    end
    
    % Set Low Power feature
    function set.LowPower(obj, value)
        validateattributes(value, {'helperBLEMeshLowPowerNode'}, ...
        {'scalar'}, mfilename, 'LowPower');
        obj.LowPower = value;
    end
    
    % Provides an interface between access layer and transport layer by
    % pushing access message in the transport queue
    function pushAccessMessage(obj, accessMessage, src, dst, ttl)
        % Validate the input source address field. Refer Mesh Profile
        % v1.0.1 of Bluetooth Specification | Section 3.4.4.6
        ble.internal.validateHex(src, 4, 'src')
        
        % Validate the input destination address field. Refer Mesh Profile
        % v1.0.1 of Bluetooth Specification | Section 3.4.4.7
        ble.internal.validateHex(dst, 4, 'dst')
        
        % Validate the input time to live field. Refer Mesh Profile v1.0.1
        % of Bluetooth Specification | Section 3.6.4.4
        validateattributes(ttl, {'numeric'}, {'integer', 'nonempty', ...
            'nonnegative', '<=', 127}, mfilename, 'ttl');
        
        % Accept the access message as either row or column vector
        if isrow(accessMessage)
            accessMessage = accessMessage';
        end
        
        % Pre-allocate the size of access data
        accessData = zeros(5+length(accessMessage), 1);
        accessData(1:2) = hex2dec(reshape(src, 2, [])');
        accessData(3:4) = hex2dec(reshape(dst, 2, [])');
        accessData(5) = ttl;
        accessData(6:end) = accessMessage;
        
        % Bluetooth mesh node receives access message
        if ~isempty(accessMessage)
            % Store access data in the transport queue
            obj.pTransportQueue.enQueue(accessData);
        end
    end
    
    % Provides an interface between transport layer and network layer
    function [txMessage, txCtl, txSrcAddr, txDstAddr, ttl] = run(obj, ...
            rxMessage, rxCtl, rxSrcAddr, rxDstAddr)
        % Initialize
        txMessage = zeros(1, 0);
        txCtl = -1;
        txSrcAddr = '0000';
        txDstAddr = '0000';
        ttl = 0;
        obj.pAccessMessageReceived = false;
        
        % Bluetooth mesh node with Low Power and Friend features disabled
        if strcmpi(obj.Feature, 'None')
            % Bluetooth mesh node receives either transport data or control
            % message
            if (~isempty(rxMessage)) && (rxCtl ~= -1)
                obj.ReceivedMsgs = obj.ReceivedMsgs + 1;
                obj.BytesReceived = obj.BytesReceived + length(rxMessage);
                % Bluetooth mesh node receives transport control message
                if rxCtl == 1
                    fprintf(['helperBLEMeshTransportLayer class does not' ...
                        ' support Friendship control messages']);
                % Bluetooth mesh node receives transport data message
                else
                    accessMessage = ...
                        helperBLEMeshTransportDataMessageDecode(rxMessage);
                    obj.pDecodedAccessMessage = accessMessage;
                    obj.pDecodedSRC = rxSrcAddr;
                    obj.pDecodedDST = rxDstAddr;
                    obj.pAccessMessageReceived = true;
                end
            % Bluetooth mesh node sends transport data message
            else
                emptyFlag = obj.pTransportQueue.isEmpty();
                if ~emptyFlag
                    obj.TransmittedMsgs = obj.TransmittedMsgs + 1;
                    [~, accessData] = obj.pTransportQueue.deQueue();
                    txSrcAddr = reshape(dec2hex(accessData(1:2), 2)', 1, []);
                    txDstAddr = reshape(dec2hex(accessData(3:4), 2)', 1, []);
                    ttl = accessData(5);
                    % Add transport layer header to the access message
                    txMessage = helperBLEMeshTransportDataMessage(accessData(6:end));
                    txCtl = 0;
                    obj.BytesTransmitted = obj.BytesTransmitted + length(txMessage);
                end
            end
        % Friend feature enabled at Bluetooth mesh node
        elseif strcmpi(obj.Feature, 'Friend')
            % Initialize
            meshMsg = zeros(1, 0);
                
            % Friend node receives friend poll message from the Low Power
            % node             
            if strcmp(rxSrcAddr, obj.FriendAddress)
                if (~isempty(rxMessage)) && (rxCtl == 1)
                    meshMsg = rxMessage;
                    obj.ReceivedLPNMsgs = obj.ReceivedLPNMsgs + 1;
                    obj.BytesReceived = obj.BytesReceived + length(rxMessage);
                end
            else
                % Friend node receives transport data message destined to
                % itself from the Bluetooth mesh node
                if strcmp(rxDstAddr, obj.ElementAddress) || any(strcmp(rxDstAddr, obj.SubscriptionAddresses))
                    if (~isempty(rxMessage)) && (rxCtl == 0)
                        obj.ReceivedMsgs = obj.ReceivedMsgs + 1;
                        obj.BytesReceived = obj.BytesReceived + length(rxMessage);
                        accessMessage = ...
                            helperBLEMeshTransportDataMessageDecode(rxMessage);
                        obj.pDecodedAccessMessage = accessMessage;
                        obj.pAccessMessageReceived = true;
                        rxCtl = -1;
                    end
                % Friend node receives transport data message destined for
                % the Low Power node from the Bluetooth mesh node
                else
                    if (~isempty(rxMessage)) && (rxCtl == 0)
                        meshMsg = rxMessage;
                        obj.ReceivedMsgs = obj.ReceivedMsgs + 1;
                        obj.BytesReceived = obj.BytesReceived + length(rxMessage);
                    end
                end
            end      
           
            [txMessage, txCtl] = obj.Friend.run(meshMsg, rxCtl);
            
            % Friend node sends transport data message to Bluetooth mesh
            % node
            if isempty(txMessage) || txCtl == -1
                emptyFlag = obj.pTransportQueue.isEmpty();
                if ~emptyFlag
                    obj.TransmittedMsgs = obj.TransmittedMsgs + 1;
                    [~, accessData] = obj.pTransportQueue.deQueue();
                    txSrcAddr = reshape(dec2hex(accessData(1:2), 2)', 1, []);
                    txDstAddr = reshape(dec2hex(accessData(3:4), 2)', 1, []);
                    ttl = accessData(5);
                    % Add transport layer header to the access message
                    txMessage = helperBLEMeshTransportDataMessage(accessData(6:end));
                    txCtl = 0;
                    obj.BytesTransmitted = obj.BytesTransmitted + length(txMessage);
                end
            % Friend node sends either transport data or friend update
            % message to Low Power node
            else
                obj.TransmittedMsgs = obj.TransmittedMsgs + 1;
                txSrcAddr = obj.ElementAddress;
                txDstAddr = obj.FriendAddress;
                ttl = 0;
                obj.BytesTransmitted = obj.BytesTransmitted + length(txMessage);
            end
        % Low power feature enabled at Bluetooth mesh node
        else
            % Initialize
            frndMsg =  zeros(1, 0);
            
            % Low power node is in listening state
            if (obj.LowPower.pState == 1)
                % Low power node receives transport data message from other
                % than Friend node
                if (~strcmp(rxSrcAddr, obj.FriendAddress))
                    rxCtl = -1;
                % Low power node receives friend update or transport data
                % message from the Friend node
                else
                    frndMsg =  rxMessage;
                end

                if (~isempty(frndMsg)) && (rxCtl ~= -1)
                    obj.ReceivedMsgs = obj.ReceivedMsgs + 1;
                    obj.BytesReceived = obj.BytesReceived + length(frndMsg);
                    if rxCtl == 0
                        accessMessage = helperBLEMeshTransportDataMessageDecode(frndMsg);
                        obj.pDecodedAccessMessage = accessMessage;
                        obj.pDecodedSRC = rxSrcAddr;
                        obj.pDecodedDST = rxDstAddr;
                        obj.pAccessMessageReceived = true;
                    end
                end
            end
            
            [txMessage, txCtl] = obj.LowPower.run(frndMsg, rxCtl);
            
            % Low power node sends transport data message to Bluetooth mesh
            % in idle state
            if obj.LowPower.pState == 0
                emptyFlag = obj.pTransportQueue.isEmpty();
                if ~emptyFlag
                    obj.TransmittedMsgs = obj.TransmittedMsgs + 1;
                    [~, accessData] = obj.pTransportQueue.deQueue();
                    txSrcAddr = reshape(dec2hex(accessData(1:2), 2)', 1, []);
                    txDstAddr = reshape(dec2hex(accessData(3:4), 2)', 1, []);
                    ttl = accessData(5);
                    % Add transport layer header to the access message
                    txMessage = helperBLEMeshTransportDataMessage(accessData(6:end));
                    txCtl = 0;
                    obj.BytesTransmitted = obj.BytesTransmitted + length(txMessage);
                end
            % Low power node sends friend poll message to Friend node
            elseif obj.LowPower.pState == 2
                obj.TransmittedMsgs = obj.TransmittedMsgs + 1;
                txSrcAddr = obj.ElementAddress;
                txDstAddr = obj.FriendAddress;
                ttl = 0;
                obj.BytesTransmitted = obj.BytesTransmitted + length(txMessage);
            end
        end
    end
end
end
