source: ResearchApps/PHY/WARPLAB/WARPLab7/M_Code_Reference/classes/wl_transport_eth_udp_java.m

Last change on this file was 4818, checked in by welsh, 8 years ago

Added version checking for MEX transport; Added error checking to help with version errors.

File size: 23.8 KB
RevLine 
[4332]1%-------------------------------------------------------------------------
2% WARPLab Framework
[2149]3%
[4332]4% Copyright 2013, Mango Communications. All rights reserved.
5%           Distributed under the WARP license  (http://warpproject.org/license)
[2149]6%
[4332]7% Chris Hunter (chunter [at] mangocomm.com)
8% Patrick Murphy (murphpo [at] mangocomm.com)
9% Erik Welsh (welsh [at] mangocomm.com)
10%-------------------------------------------------------------------------
[2149]11
[1989]12classdef wl_transport_eth_udp_java < wl_transport & handle_light
[2149]13% Java-based Ethernet UDP Transport for unicast traffic
[1975]14% User code should not use this object directly-- the parent wl_node will
15%  instantiate the appropriate transport object for the hardware in use
[2149]16
17%******************************** Properties **********************************
18
[1915]19    properties (SetAccess = public)
[4416]20        timeout;        % Maximum time spent waiting before retransmission
[1915]21    end
[2149]22
[1915]23    properties (SetAccess = protected, Hidden = true)
[4416]24        sock;           % UDP socket
25       
26        j_address;      % Java address   (statically allocated for performance reasons)
27        send_pkt;       % DatagramPacket (statically allocated for performance reasons)
28        recv_pkt;       % DatagramPacket (statically allocated for performance reasons)
29       
30        status;         % Status of UDP socket
31        maxPayload;     % Maximum payload size (e.g. MTU - ETH/IP/UDP headers)
[1915]32    end
[2149]33
[4416]34    properties (SetAccess = protected)
35        address;        % IP address of destination
36        port;           % Port of destination
37    end
38   
[1915]39    properties (SetAccess = public)
[4416]40        hdr;            % Transport header object
41        rxBufferSize;   % OS's receive buffer size in bytes
[1915]42    end
[2149]43
[4309]44    properties(Hidden = true, Constant = true)
45        % These constants define specific command IDs used by this object.
46        % Their C counterparts are found in wl_transport.h
[4332]47        GRP                            = 'transport';
48        CMD_PING                       = 1;                % 0x000001
[4784]49        CMD_PAYLOADSIZETEST            = 2;                % 0x000002
[2069]50       
[4784]51        CMD_NODEGRPID_ADD              = 16;               % 0x000010
52        CMD_NODEGRPID_CLEAR            = 17;               % 0x000011
53       
[4309]54        TRANSPORT_NOT_READY_MAX_RETRY  = 50;
55        TRANSPORT_NOT_READY_WAIT_TIME  = 0.1;
[1948]56    end
[2149]57
58%********************************* Methods ************************************
59
[1915]60    methods
[1931]61        function obj = wl_transport_eth_udp_java()
[4416]62            import java.net.InetAddress
63           
[4332]64            obj.hdr         = wl_transport_header;
[1915]65            obj.hdr.pktType = obj.hdr.PKTTYPE_HTON_MSG;
[4332]66            obj.status      = 0;
67            obj.timeout     = 1;
[4416]68            obj.maxPayload  = 1000;    % Sane default. This gets overwritten by CMD_PAYLOADSIZETEST command.
69            obj.port        = 0;
70            obj.address     = '10.0.0.0';
71            obj.j_address   = InetAddress.getByName(obj.address);
[4332]72           
[4416]73            obj.create_internal_pkts();
74           
[1915]75            obj.checkSetup();
76        end
77       
78        function checkSetup(obj)
[4332]79            % Currently not implemented
[1915]80        end
[2149]81
[4818]82        function setMaxPayload(obj, value)
83            if (isempty(value))
84               error('wl_transport_eth_udp_java:setMaxPayload', 'setMaxPayload requires a non-empty argument.');
85            else
86                obj.maxPayload = value;
87               
88                % Update the send / recv packets
89                obj.update_pkt_length();
90            end
[2149]91        end
92       
93        function out = getMaxPayload(obj)
[2856]94            out = double(obj.maxPayload);
[2149]95        end
96
[4416]97        function setAddress(obj, value)
98            import java.net.InetAddress
99           
100            if(ischar(value))
101                obj.address = value;
102            else
103                obj.address = obj.int2IP(value);
104            end
105           
106            % Update the java address
107            obj.j_address    = InetAddress.getByName(obj.address);
108           
109            % Update the send / recv packets
110            obj.update_pkt_address();
111        end
112       
113        function out = getAddress(obj)
114            out = obj.address;
115        end
116
117        function setPort(obj, value)
118            obj.port = value;
119           
120            % Update the send / recv packets
121            obj.update_pkt_port();
122        end
123       
124        function out = getPort(obj)
125            out = obj.port;
126        end
127
[4332]128        function open(obj, varargin)
129            % varargin{1}: (optional) IP address
130            % varargin{2}: (optional) port
[1931]131
[4332]132            REQUESTED_BUF_SIZE = 2^22;
133           
[1931]134            import java.io.*
135            import java.net.DatagramSocket
136            import java.net.DatagramPacket
[4416]137           
[1915]138            if(nargin==3)
[4416]139               obj.setAddress(varargin{1});
140               obj.setPort(varargin{2});
[1915]141            end
[1931]142           
[1944]143            obj.sock = DatagramSocket();
[4332]144           
[1931]145            obj.sock.setSoTimeout(1);
146            obj.sock.setReuseAddress(1);
[4332]147            obj.sock.setBroadcast(true);
148            obj.sock.setSendBufferSize(REQUESTED_BUF_SIZE);           
[1961]149            obj.sock.setReceiveBufferSize(REQUESTED_BUF_SIZE); 
[4332]150 
[1952]151            x = obj.sock.getReceiveBufferSize();
[4332]152           
[1961]153            if(x < REQUESTED_BUF_SIZE)
[1952]154                fprintf('OS reduced recv buffer size to %d\n', x);
155            end
[4332]156           
157            obj.rxBufferSize = x;
158            obj.status       = 1;
[1915]159        end
160       
[4332]161        function out = procCmd(obj, nodeInd, node, cmdStr, varargin)
162            % wl_node procCmd(obj, nodeInd, node, varargin)
163            %     obj:       Node object (when called using dot notation)
164            %     nodeInd:   Index of the current node, when wl_node is iterating over nodes
165            %     node:      Current node object
166            %     cmdStr:    Command string of the interface command
167            %     varargin:
168            %         [1:N}  Command arguments
169            %
170            out    = [];
171           
[1915]172            cmdStr = lower(cmdStr);
173            switch(cmdStr)
[4332]174           
175                %---------------------------------------------------------
[1915]176                case 'ping'
[1975]177                    % Test to make sure node can be accessed via this
178                    % transport
[1915]179                    %
180                    % Arguments: none
181                    % Returns: true if board responds; raises error otherwise
[4332]182                    %
[1948]183                    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_PING));
[1975]184                    node.sendCmd(myCmd);
[1915]185                    out = true; %sendCmd will timeout and raise error if board doesn't respond
[2069]186                   
[4332]187                %---------------------------------------------------------
[1964]188                case 'payload_size_test'
[4332]189                    % Determine objects maxPayload parameter
[1975]190                    %
191                    % Arguments: none
[1997]192                    % Returns: none
[4332]193                    %
194                    configFile = which('wl_config.ini');
[1977]195                   
[1997]196                    if(isempty(configFile))
[2865]197                       error('cannot find wl_config.ini. please run wl_setup.m'); 
[1997]198                    end
[4332]199                   
[4375]200                    readKeys = {'network', '', 'max_transport_payload_size', ''};
201                    max_transport_payload_size = inifile(configFile, 'read', readKeys);
202                    max_transport_payload_size = str2num(max_transport_payload_size{1});
[1997]203                   
[4375]204                    % Determine the payloads to test
205                    payloadTestSizes = [];
206
207                    for i = [1000 1470 5000 8966]
208                        if (i < max_transport_payload_size) 
209                            payloadTestSizes = [payloadTestSizes, i];
210                        end
[1997]211                    end
212                   
[4375]213                    payloadTestSizes = [payloadTestSizes, max_transport_payload_size];
214                   
[4386]215                    % WARPLab Header is (see http://warpproject.org/trac/wiki/WARPLab/Reference/Architecture/WireFormat ):
216                    %   - 2 byte pad
217                    %   - Transport header
218                    %   - Command header
219                    payloadTestSizes = floor((payloadTestSizes - (sizeof(node.transport.hdr) + sizeof(wl_cmd) + 2)) / 4);
220                   
[1977]221                    for index = 1:length(payloadTestSizes)
[4386]222                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_PAYLOADSIZETEST));
[1977]223                        myCmd.addArgs(1:payloadTestSizes(index));
[1997]224                        try
225                            resp = node.sendCmd(myCmd);
[2149]226                            obj.setMaxPayload( resp.getArgs );
[4375]227                            if(obj.getMaxPayload < (payloadTestSizes(index) * 4))
[1997]228                               break; 
229                            end
230                        catch ME
231                            break 
[1977]232                        end
233                    end
[2069]234                   
[4332]235                %---------------------------------------------------------
[2041]236                case 'add_node_group_id'
237                    % Adds a Node Group ID to the node so that it can
238                    % process broadcast commands that are received from
239                    % that node group.
240                    %
241                    % Arguments: (uint32 NODE_GRP_ID)
242                    % Returns: none
243                    %
244                    % NODE_GRP_ID: ID provided by wl_node_grp
[4332]245                    %
246                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_ADD));
247                    myCmd.addArgs(varargin{1});
[2041]248                    node.sendCmd(myCmd);
[2069]249                   
[4332]250                %---------------------------------------------------------
[2041]251                case 'clear_node_group_id'
252                    % Clears a Node Group ID from the node so it can ignore
253                    % broadcast commands that are received from that node
254                    % group.
255                    %
256                    % Arguments: (uint32 NODE_GRP_ID)
257                    % Returns: none
258                    %
259                    % NODE_GRP_ID: ID provided by wl_node_grp
[4332]260                    %
261                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_CLEAR));
262                    myCmd.addArgs(varargin{1});
[2041]263                    node.sendCmd(myCmd);
[2055]264                   
[4332]265                %---------------------------------------------------------
[1915]266                otherwise
[2865]267                    error('unknown command ''%s''',cmdStr);
[1915]268            end
269           
[4332]270            if((iscell(out) == 0) && (numel(out) ~= 1))
[1915]271                out = {out}; 
272            end     
273        end
274       
275        function close(obj)
[1935]276            if(~isempty(obj.sock))
277                try
278                    obj.sock.close();
279                catch closeError
[2865]280                    warning( 'Error closing socket; java error was %s', closeError.message)
[1935]281                end
[1931]282            end
283            obj.status=0;
[1915]284        end   
[2149]285
[1915]286        function delete(obj)
287            obj.close();
288        end
[2149]289
[1915]290        function flush(obj)
[4332]291            % Currently not implemented
[1915]292        end
[4332]293    end % methods
[2149]294
[1915]295    methods (Hidden = true)
[4309]296        function reply = send(obj, send_data, response, varargin)
297            % send_data  : Data to be sent to the node
298            % response   : Does the transmission require a response from the node
299            % varargin{1}: (optional)
300            %     - increment the transport header; defaults to true if not specified
301            %
[1931]302            import java.io.*
303            import java.net.DatagramSocket
304            import java.net.DatagramPacket
305
[4309]306            % Initialize variables
307            maxAttempts       = 2;                                   % Maximum times the transport will re-try a packet
308            payload           = uint32(send_data);                   % Change data to 32 bit unsigned integers for transmit
309            obj.hdr.msgLength = ((length(payload)) * 4);             % Length in bytes (4 bytes / sample)
310            reply             = [];                                  % Initialize array for response from the node
311            robust            = response;
312
313            % Process command line arguments
314            increment_hdr     = true;
315
316            if (nargin == 4)
317               increment_hdr  = varargin{1};
[1915]318            end
319           
[4309]320            % Set the appropriate flags in the header           
[1915]321            if(robust)
[4309]322               obj.hdr.flags = bitset(obj.hdr.flags, 1, 1);
[1915]323            else
[4309]324               obj.hdr.flags = bitset(obj.hdr.flags, 1, 0);
[1915]325            end
[4309]326
327            % Increment the header and serialize all the data into a uint32 array
328            if (increment_hdr)
329                obj.hdr.increment;
330            end
[1915]331           
[4309]332            % Format the data / address arguments for transmission
333            data     = [obj.hdr.serialize, payload];                                      % Get a unified array with the header and data
334            data8    = [zeros(1,2,'uint8') typecast(swapbytes(uint32(data)), 'uint8')];   % Change to uint8 and pad by 2 bytes so payload is 32 bit aligned w/ Eth header
[4416]335           
336            pkt_send = obj.send_pkt;                                                      % Get pointer to statically allocated packet
337            pkt_send.setData(data8, 0, length(data8));                                    % Update the send packet with the correct data
338            % pkt_send = DatagramPacket(data8, length(data8), obj.j_address, obj.port);     % Create a java DatagramPacket to send over the network
[1931]339
[4309]340            % Send the packet
[1931]341            obj.sock.send(pkt_send);
[1915]342           
[4309]343            % Wait to receive reply from the board               
344            if(robust == 1)
345                hdr_length       = obj.hdr.length;
[4416]346                pkt_recv         = obj.recv_pkt;
[4309]347                currTx           = 1;
348                numWaitRetries   = 0;
[1915]349                receivedResponse = 0;
[4309]350                currTime         = tic;
[1931]351
352                while (receivedResponse == 0)
353                    try
354                        obj.sock.receive(pkt_recv);
355                        recv_len = pkt_recv.getLength;
356                    catch receiveError
357                        if ~isempty(strfind(receiveError.message,'java.net.SocketTimeoutException'))
[4309]358                            % Timeout receiving; do nothing
[1931]359                            recv_len = 0;
360                        else
[4309]361                            fprintf('%s.m--Failed to receive UDP packet.\nJava error message follows:\n%s',mfilename,receiveError.message);
[1931]362                        end
363                    end
[1915]364                   
[4309]365                    % If we have a packet, then process the contents
[1931]366                    if(recv_len > 0)
367                        recv_data8 = pkt_recv.getData;
[4309]368                        reply8     = [recv_data8(3:recv_len) zeros(mod(-(recv_len - 2), 4), 1, 'int8')].';
369                        reply      = swapbytes(typecast(reply8, 'uint32'));
370
371                        % Check the header to see if this was a valid reply                       
372                        if(obj.hdr.isReply(reply(1:hdr_length)))
373                       
374                            % Check the header to see if we need to wait for the node to be ready
375                            if ( obj.hdr.isNodeReady(reply(1:hdr_length)) )
376                           
377                                % Strip off transport header to give response to caller
378                                reply  = reply((hdr_length + 1):end);
379                               
380                                if(isempty(reply))
381                                    reply = []; 
382                                end
383                               
384                                receivedResponse = 1;
385                               
386                            else
387                                % Node is not ready; Wait and try again
388                                pause( obj.TRANSPORT_NOT_READY_WAIT_TIME );               
389                                numWaitRetries = numWaitRetries + 1;
390
391                                % Send packet packet again
392                                obj.sock.send(pkt_send);
393
394                                % Check that we have not spent a "long time" waiting for samples to be ready               
395                                if ( numWaitRetries > obj.TRANSPORT_NOT_READY_MAX_RETRY )
396                                    error('wl_transport_eth_java:send:isReady', 'Error:  Timeout waiting for node to be ready.  Please check the node operation.');
397                                end
398                               
399                                reply = [];
[1915]400                            end
[4309]401                        end
[1915]402                    end
403                   
[4309]404                    % Look for timeout
405                    if ((toc(currTime) > obj.timeout) && (receivedResponse == 0))
[1982]406                        if(currTx == maxAttempts)
407                            error('wl_transport_eth_java:send:noReply','maximum number of retransmissions met without reply from node'); 
408                        end
[4309]409                       
410                        % Retry the packet
[1931]411                        obj.sock.send(pkt_send);
[4309]412                        currTx   = currTx + 1;
[1931]413                        currTime = tic;
[4309]414                    end
[1915]415                end
416            end
417        end
418       
[4416]419        function send_raw(obj, send_data, send_length)
420            % send_data   : Raw data to be sent over the socket
421            % send_length : Length of data to be sent over the socket
422            %
423            import java.io.*
424            import java.net.DatagramSocket
425            import java.net.DatagramPacket
426
427            % Format the data / address arguments for transmission
428            data8    = typecast(send_data, 'uint8');                                           % Change to uint8
429            pkt_send = obj.send_pkt;                                                           % Get pointer to statically allocated packet
430            pkt_send.setData(data8, 0, send_length);                                           % Update the send packet with the correct data
431           
432            % Send the packet
433            obj.sock.send(pkt_send);
434        end
435       
[4309]436        function resp = receive(obj, varargin)
437            % Receive all packets from the Ethernet interface and pass array
438            %     of valid responses to the caller.
439            %
440            % NOTE:  This function will strip off the transport header from the responses
441            % NOTE:  This function is non-blocking and will return an empty response if
442            %            there are no packets available.
443            %
[1931]444            import java.io.*
445            import java.net.DatagramSocket
446            import java.net.DatagramPacket
[4309]447
448            % Initialize variables
449            hdr_length  = obj.hdr.length;
[4416]450            pkt_recv    = obj.recv_pkt;
[4309]451            done        = false;
452            resp        = [];
[1931]453           
[4309]454            % Receive packets           
[1915]455            while ~done
[1931]456                try
457                    obj.sock.receive(pkt_recv);
458                    recv_len = pkt_recv.getLength;
459                catch receiveError
460                    if ~isempty(strfind(receiveError.message,'java.net.SocketTimeoutException'))
[4309]461                        % Timeout receiving; do nothing
[1931]462                        recv_len = 0;
463                    else
464                        fprintf('%s.m--Failed to receive UDP packet.\nJava error message follows:\n%s',mfilename,receiveError.message);
[1915]465                    end
[1931]466                end
467               
[4309]468                % If we have a packet, then process the contents
[1931]469                if(recv_len > 0)
[4309]470                    recv_data8 = pkt_recv.getData;
471                    reply8     = [recv_data8(3:recv_len) zeros(mod(-(recv_len - 2), 4), 1, 'int8')].';
472                    reply      = swapbytes(typecast(reply8, 'uint32'));
473                   
474                    if(obj.hdr.isReply(reply(1:hdr_length)))
475                        % Strip off transport header to give response to caller
476                        reply  = reply((hdr_length + 1):end);
477                       
[1931]478                        if(isempty(reply))
479                            reply = [];
480                        end
[4309]481                       
482                        resp = [resp, reply];                       
483
484                        done = true;
[1931]485                    end
[1915]486                else
487                    done = true;
488                end
489            end
490        end
491       
[4416]492        function [recv_len, data] = receive_raw(obj, varargin)
493            % Receive raw data from the Ethernet interface
494            %
495           
496            % Initialize variables
497            pkt_recv    = obj.recv_pkt;
498            data        = [];
499
500            % Try to receive a packet
501            try
502                obj.sock.receive(pkt_recv);
503                recv_len = pkt_recv.getLength;
504            catch receiveError
505                if ~isempty(strfind(receiveError.message, 'java.net.SocketTimeoutException'))
506                    recv_len = 0;
507                else
508                    fprintf('%s.m--Failed to receive UDP packet.\nJava error message follows:\n%s', mfilename, receiveError.message);
509                end
510            end
511
512            % If we have a packet, then process the contents
513            if(recv_len > 0)
514                recv_data8 = pkt_recv.getData;
515                reply8     = [recv_data8(3:recv_len) zeros(mod(-(recv_len - 2), 4), 1, 'int8')].';
516                data       = swapbytes(typecast(reply8, 'uint32'));
517            end
518        end
519       
[1915]520        function dottedIPout = int2IP(obj,intIn)
521            addrChars(4) = mod(intIn, 2^8);
522            addrChars(3) = mod(bitshift(intIn, -8), 2^8);
523            addrChars(2) = mod(bitshift(intIn, -16), 2^8);
524            addrChars(1) = mod(bitshift(intIn, -24), 2^8);
[4309]525            dottedIPout  = sprintf('%d.%d.%d.%d', addrChars);
[1915]526        end
[2149]527
[1915]528        function intOut = IP2int(obj,dottedIP)
529            addrChars = sscanf(dottedIP, '%d.%d.%d.%d')';
[4309]530            intOut    = 2^0 * addrChars(4) + 2^8 * addrChars(3) + 2^16 * addrChars(2) + 2^24 * addrChars(1);
[1915]531        end
[4416]532       
533        function create_internal_pkts(obj)
534            import java.io.*
535            import java.net.DatagramPacket
536
537            send_pkt_len     = obj.getMaxPayload();
538            send_data        = [zeros(1, send_pkt_len, 'int8')];
539            obj.send_pkt     = DatagramPacket(send_data, send_pkt_len, obj.j_address, obj.port);
540
541            recv_pkt_len     = send_pkt_len + 100;
542            recv_data        = [zeros(1, recv_pkt_len, 'int8')];
543            obj.recv_pkt     = DatagramPacket(recv_data, recv_pkt_len);
544        end
545       
546        function update_pkt_port(obj)
547            obj.send_pkt.setPort(obj.port);       
548        end
549       
550        function update_pkt_address(obj)
551            obj.send_pkt.setAddress(obj.j_address);
552        end
553       
554        function update_pkt_length(obj)
555            send_pkt_len     = obj.getMaxPayload();
556            send_data        = zeros(1, send_pkt_len, 'int8');
557            obj.send_pkt.setData(send_data);
558            obj.send_pkt.setLength(send_pkt_len);
559       
560            recv_pkt_len     = send_pkt_len + 100;
561            recv_data        = zeros(1, recv_pkt_len, 'int8');
562            obj.recv_pkt.setData(recv_data);
563            obj.recv_pkt.setLength(recv_pkt_len);
564        end
[1915]565    end
[4332]566end % classdef
Note: See TracBrowser for help on using the repository browser.