classdef helperBLEMeshFriendNode < handle
%helperBLEMeshFriendNode Create an object for Bluetooth mesh Friend node 
%   MESHFN = helperBLEMeshFriendNode creates a Bluetooth mesh Friend node
%   object, with properties and methods related to Friend node.
%
%   MESHFN = helperBLEMeshFriendNode(Name, Value) creates a Bluetooth
%   mesh Friend node 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).
%
%   helperBLEMeshFriendNode property:
%
%   FriendTimer  - Friend timer object

%   Copyright 2019-2020 The MathWorks, Inc.

%#codegen

properties 
    %FriendTimer Friend timer object
    %   Specify this property as an object of type <a
    %   href="matlab:help('helperBLEMeshFriendTimer')">helperBLEMeshFriendTimer</a>.
    %   Friend timer fields are populated using settings of this object.
    %   The default value is an object of type 'helperBLEMeshFriendTimer'
    %   with all properties set to their default values.
    FriendTimer
end
    
properties (Access = private)
    %pStoredMsg Previous message sent by the Friend node. It is used to
    %resend the previous message if the Low Power node did not receive the
    %previous message
    pStoredMsg
    
    %pStoredCTL Previous control field sent by the Friend node. It is used
    %to resend the previous control field if the Low Power node did not
    %receive the previous control field
    pStoredCTL = -1
    
    %pFSN Friend Sequence Number, FSN of the previously received message
    %from the Low Power node
    pFSN = -1
    
    %pRecFSN FSN of the currently received message from the Low Power node
    pRecFSN = 0
    
    %pFriendQueue Queue for storing transport layer messages destined to
    %Low Power node
    pFriendQueue
end

properties (Hidden, SetAccess = private)
    %pState Defines the state of Friend node
    %   1 - Listen state
    %   2 - Transmission state
    %   3 - Friendship termination state
    pState = 1
end

properties (SetAccess = private)
    %ListenTime Time for which the Friend node is in listen state
    ListenTime = 0
    
    %TransmitToListen Number of transitions from transmission to listen
    %state
    TransmitToListen = 0
    
    %ListenToTransmit Number of transitions from listen to transmission
    %state
    ListenToTransmit = 0
    
    %NResync Number of times Friend node is re-synced with Low Power node
    NResync = 0
end
    
methods
    function obj = helperBLEMeshFriendNode(varargin)
        % Name-value pairs
        for idx = 1:2:nargin
            obj.(char(varargin{idx})) = varargin{idx+1};
        end
        % Initialize variable sized property (for codegen)
        obj.pStoredMsg = zeros(1, 0);
        % Create a Bluetooth mesh Friend timer object with default values
        obj.FriendTimer = helperBLEMeshFriendTimer();
        % Create a Bluetooth mesh Friend queue object with queue size 64.
        % The minimum number of Lower Transport PDUs that the Friend node
        % can store in its Friend queue for each element of the Low Power
        % node is (2, 4, 8, 16, 32, 64, 128). Refer Mesh Profile v1.0.1 of
        % Bluetooth Specification | Section 3.6.5.3
        maxQueueSize = 64;
        maxElementSize = 50;
        obj.pFriendQueue = helperBluetoothQueue(maxQueueSize, maxElementSize);
    end
    
    function set.pStoredMsg(obj, value)
        obj.pStoredMsg = zeros(12,1);
        obj.pStoredMsg = value;
    end
    
    % Set Friend timer
    function set.FriendTimer(obj, value)
        validateattributes(value, {'helperBLEMeshFriendTimer'}, {'scalar'}, ...
            mfilename, 'FriendTimer');
        obj.FriendTimer = value;
    end
    
    % Provides an interface between Friend feature and the transport layer
    function [txMessage, txCtl] = run(obj, rxMessage, rxCtl)
        % Initialize
        txMessage = zeros(1, 0);
        txCtl = -1;
        
        % Reset rxCTL when Friend node is not in listen state
        if (obj.pState ~= 1)
            rxCtl = -1;
        end
        
        previousState = obj.pState;
        obj.pState = obj.FriendTimer.runFNTimer(rxCtl);
        
        % Transition from transmission to listen state
        if previousState == 2 && obj.pState == 1
            obj.TransmitToListen = obj.TransmitToListen + 1;
        end
        
        % Transition from listen to transmission state
        if previousState == 1 && obj.pState == 2
            obj.ListenToTransmit = obj.ListenToTransmit + 1;
        end
        
        % Node state
        switch obj.pState
            % Listen
            case 1
                % Update the listen time
                obj.ListenTime = obj.ListenTime + obj.FriendTimer.StepTime;
                
                if (~isempty(rxMessage)) && (rxCtl ~= -1)
                    % Friend node receives Friend poll message
                    if rxCtl == 1
                        [~, obj.pRecFSN] = ...
                         helperBLEMeshTransportControlMessageDecode(rxMessage);
                    % Friend node receives transport data message destined
                    % to Low Power node
                    else
                        obj.pFriendQueue.enqueue(rxMessage);
                    end
                end
            % Transmission
            case 2
                % If previous FSN and current received FSN are equal, then
                % resend the previous message and control field. Refer Mesh
                % Profile v1.0.1 of Bluetooth Specification | Section
                % 3.6.6.3.2
                if obj.pRecFSN == obj.pFSN
                    txMessage = obj.pStoredMsg;
                    txCtl = obj.pStoredCTL;
                % If previous FSN and current received FSN are not equal,
                % then send the oldest entry in the Friend queue
                else
                    obj.pFSN = obj.pRecFSN;
                    emptyFlag = obj.pFriendQueue.isEmpty();
                    % Send the transport data message
                    if ~emptyFlag
                        [~, txMessage] = obj.pFriendQueue.dequeue();
                        txCtl = 0;
                    % Send the friend update message
                    else
                        txMessage = ...
                            helperBLEMeshTransportControlMessage('Friend update', false);
                        txCtl = 1;
                    end
                    obj.pStoredMsg = txMessage;
                    obj.pStoredCTL = txCtl;
                end
            % Terminate
            case 3
                % Update the re-sync count
                obj.NResync = obj.NResync + 1;
        end
    end
end
end
