classdef MessageReceiveFunctionInfo < rtw.pil.QueuedIOFunctionInfo
    %MESSAGERECEIVEFUNCTIONINFO: representation for Message Receiver interface 
    
    %   Copyright 2017-2019 The MathWorks, Inc.
    
        
    properties (Access = private)
        LocalQueueID = -1; %Used for Model Block S-function local receive queues.
    end    
    
    properties (Constant)
        %Fix for g1597392. Return correct AUTOSAR status values. These
        %should be moved to the target ultimately.
        AUTOSAR_OK = 0; %0x00
        AUTOSAR_NO_DATA = 131; %0x83
        AUTOSAR_LOST_DATA = 64; %0x40
        
        %Status defined by message service infrastructure;
        %the values shall match with ServiceReturnCode 
        %(defined in SLMsgSpec.hpp)
        SVC_OK = 0;
        SVC_NOT_OK = 1;
        SVC_OVERFLOWED = 2;
    end
    
    
    methods (Access = {?rtw.pil.CSFunctionInfoFactory})
        % Only allow object construction by the factory class
        function this = MessageReceiveFunctionInfo(fcnName, rtmTypeIdentifier, argType, timing, variantInfo,...
                tid,...
                fcnId,...
                isDefined,...
                isAUTOSAR, ...
                portNumber, ...
                queueID, ...
                mf0Instance)
            
           % call superclass constructor
            this@rtw.pil.QueuedIOFunctionInfo(fcnName, rtmTypeIdentifier, argType, timing, variantInfo,...
                tid,...
                fcnId,...
                isDefined,...
                isAUTOSAR, ...
                portNumber, ...
                'y', ...
                'OUTPUT', ...
                mf0Instance);
            
            this.LocalQueueID = queueID; %Zero based.
        end
    end
    
    methods
        % Write out call to the S-function Message Receive API
        % This includes determining the queue status, and extracting the
        % payload so it can be sent to the target.
        function writeCallToFunction(this, writer, simStructArg, argsVarName, ~)

            assert(this.PortNumber ~= -1, 'Port number shouldn''t be -1');
            portID = this.PortNumber;
            
            writer.wBlockStart(''); % scope around each argument

            writer.wLine('uint_T status = %d;', this.SVC_NOT_OK);
            writer.wComment('Pop a message from queue, and extract it''s payload');
            writer.wLine('status = slmsg_ssRecvDataInSIL(%s%d, %s);', ...
               simStructArg, portID, argsVarName);
            writer.wComment('Set return value');
            writer.wBlockStart('if (%d == status)', this.SVC_OK);
            writer.wComment('Receive operation succeeds');
            writer.wLine('returnArg = %d;', this.AUTOSAR_OK); 
            writer.wBlockMiddle('else if(%d == status)', this.SVC_OVERFLOWED);
            writer.wComment('Receive queue overflow detected');
            writer.wLine('returnArg = %d;', this.AUTOSAR_LOST_DATA); %Report Overflow
            writer.wBlockEnd(); 
            
            writer.wBlockEnd();
        end
        
        %Define any data needed for the Message Receive call. We need the
        %payload variable and a return status argument
        function argPointerAccessString = defineArgsForSLCall(this, writer, argVarName)
            
            argPointerAccessString  = ''; %Not Used.
            writer.writeLine('void *%s;', argVarName);
            writer.writeLine('uint_T returnArg = %d;', this.AUTOSAR_NO_DATA); %Define return value and initialize it
        end
        
        % Write out code needed to process the payload to allow it to be
        % sent to the target.
        function writeSfunctionDataAssignmentForSLCall(this, sfunObj, writer, argVarName)            
            
            [~, argDataInterface] = this.getPrimaryArgDataElement();
            writer.wBlockStart('if (ssGetPWork(S) != NULL)');
            argPtr = sprintf('%s', argVarName);
            
            pWorkIdx = sfunObj.getCallerFunctionArgumentPWorkIdx(this, argDataInterface);
            sfunObj.getPWorkValue('void', argPtr , pWorkIdx, false);
            writer.wBlockEnd; %if (ssGetPWork(S) != NULL)
        end        
    end
    
    methods (Access = 'protected')
        %Find the payload argument from the list of data interfaces. Ignore
        %the return argument. Return as soon the argument has been found.
        function [primaryArgDataElement, argDataInterface] = getPrimaryArgDataElement(this)
            args = this.getAllCodeInfoArgs;
            for codeInfoArgIdx = 1:length(args)
                
                arg = args(codeInfoArgIdx);
                if ~strcmp(arg.IOType, 'OUTPUT') || this.isReturnArg(arg)
                    continue;
                end
                codeArgPosition = this.getCodeArgPosition(arg);
                argDataInterface = this.getDataInterfacesForArgs(codeArgPosition);
                dataElements = this.getDataElementsForArgs(codeArgPosition);
                % the first data element has all the info we need
                primaryArgDataElement = dataElements(1);
                return; %Return when we've found the primary data element
            end
        end
    end
end
