classdef RtIOStreamLink < rtw.connectivity.RtIOStream    
%RTIOSTREAMLINK MATLAB-based RtIOStream communications for IDE's. 
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2011-2013 The MathWorks, Inc.
    
    properties (GetAccess = 'private', SetAccess = 'private')
       RecvData = []; % buffer received data
       IODataType; % data type used for I/O
       RxBufferSizeBytes; % size of target rx buffer in bytes
       RxBufferSizeMemUnits; % size of target rx buffer in mem units
       NeedToContinueBeforeRead; % boolean flag to indicate if we need to continue before reading
    end
    
    methods (Abstract, Access = 'protected')
        openStreamHook(this); % open connection
        sendHook(this, data); % send data
        data = recvHook(this); % receive data
        closeStreamHook(this); % close connection
        continueHook(this); % continue to next breakpoint
    end
    
    methods
        function this = RtIOStreamLink(rxBufferSizeBytes)       
            % store buffer size
            this.RxBufferSizeBytes = rxBufferSizeBytes;
        end                
        
        function sizeSent = send(this, data)
            %
            % assume target is ready to receive data
            %                        
            % limit send according to target rx buffer
            sizeSent = min(length(data), this.RxBufferSizeMemUnits);
            % call subclass send hook
            this.sendHook(data(1:sizeSent));   
            % call subclass continue hook to process the sent data
            this.continueHook();            
            % we just continued - no need to continue again before reading
            this.NeedToContinueBeforeRead = false;
        end
        
        function data = recv(this, receiveSize)                                                                               
            %
            % assume target is ready to send data
            %                        
            if isempty(this.RecvData)     
                if this.NeedToContinueBeforeRead
                    % run the target to make the next chunk of data available
                    this.continueHook();         
                end
                % call subclass hook to receive more data
                this.RecvData = this.recvHook;
                % flag that we've read all the available data and need to 
                % continue before reading any more
                this.NeedToContinueBeforeRead = true; 
            end                                  
            dataAvail = length(this.RecvData);
            if receiveSize < dataAvail                
               % use required amount of data
               data = this.RecvData(1:receiveSize);
               this.RecvData = this.RecvData(receiveSize+1:end);               
            else
               % use all available data               
               data = this.RecvData;
               this.RecvData = [];
            end
        end
        
        function close(this)
            % call subclass hook
            this.closeStreamHook;                        
        end   
               
        function supportsOnlyByteIO = supportsOnlyByteIO(this)          %#ok<MANU>
           % word addressable I/O is supported
           supportsOnlyByteIO = false; 
        end
    end
    
    methods (Access = 'protected')
        function streamID = openHook(this, ioDataType)
            % return a valid streamID
            streamID = 0;
            % initialize data cache
            this.RecvData = [];
            % cache ioDataType (memUnitType)
            this.IODataType = ioDataType;
            % derive memUnit size in bytes
            memUnitBytes = length(typecast(cast(1, ioDataType), 'uint8'));
            % derive size of target receive buffer in memUnits
            %
            % be careful to truncate the division to match the C implementation
            % of this division on the target (integer division truncates any fractional part)
            % note: both operands should be positive values
            this.RxBufferSizeMemUnits = floor(this.RxBufferSizeBytes / memUnitBytes);
            % call subclass hook
            this.openStreamHook;
        end        
        
        function ioDataType = getIODataType(this)
            ioDataType = this.IODataType;
        end
    end
end