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, 6 years ago

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

File size: 23.8 KB
Line 
1%-------------------------------------------------------------------------
2% WARPLab Framework
3%
4% Copyright 2013, Mango Communications. All rights reserved.
5%           Distributed under the WARP license  (http://warpproject.org/license)
6%
7% Chris Hunter (chunter [at] mangocomm.com)
8% Patrick Murphy (murphpo [at] mangocomm.com)
9% Erik Welsh (welsh [at] mangocomm.com)
10%-------------------------------------------------------------------------
11
12classdef wl_transport_eth_udp_java < wl_transport & handle_light
13% Java-based Ethernet UDP Transport for unicast traffic
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
16
17%******************************** Properties **********************************
18
19    properties (SetAccess = public)
20        timeout;        % Maximum time spent waiting before retransmission
21    end
22
23    properties (SetAccess = protected, Hidden = true)
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)
32    end
33
34    properties (SetAccess = protected)
35        address;        % IP address of destination
36        port;           % Port of destination
37    end
38   
39    properties (SetAccess = public)
40        hdr;            % Transport header object
41        rxBufferSize;   % OS's receive buffer size in bytes
42    end
43
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
47        GRP                            = 'transport';
48        CMD_PING                       = 1;                % 0x000001
49        CMD_PAYLOADSIZETEST            = 2;                % 0x000002
50       
51        CMD_NODEGRPID_ADD              = 16;               % 0x000010
52        CMD_NODEGRPID_CLEAR            = 17;               % 0x000011
53       
54        TRANSPORT_NOT_READY_MAX_RETRY  = 50;
55        TRANSPORT_NOT_READY_WAIT_TIME  = 0.1;
56    end
57
58%********************************* Methods ************************************
59
60    methods
61        function obj = wl_transport_eth_udp_java()
62            import java.net.InetAddress
63           
64            obj.hdr         = wl_transport_header;
65            obj.hdr.pktType = obj.hdr.PKTTYPE_HTON_MSG;
66            obj.status      = 0;
67            obj.timeout     = 1;
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);
72           
73            obj.create_internal_pkts();
74           
75            obj.checkSetup();
76        end
77       
78        function checkSetup(obj)
79            % Currently not implemented
80        end
81
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
91        end
92       
93        function out = getMaxPayload(obj)
94            out = double(obj.maxPayload);
95        end
96
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
128        function open(obj, varargin)
129            % varargin{1}: (optional) IP address
130            % varargin{2}: (optional) port
131
132            REQUESTED_BUF_SIZE = 2^22;
133           
134            import java.io.*
135            import java.net.DatagramSocket
136            import java.net.DatagramPacket
137           
138            if(nargin==3)
139               obj.setAddress(varargin{1});
140               obj.setPort(varargin{2});
141            end
142           
143            obj.sock = DatagramSocket();
144           
145            obj.sock.setSoTimeout(1);
146            obj.sock.setReuseAddress(1);
147            obj.sock.setBroadcast(true);
148            obj.sock.setSendBufferSize(REQUESTED_BUF_SIZE);           
149            obj.sock.setReceiveBufferSize(REQUESTED_BUF_SIZE); 
150 
151            x = obj.sock.getReceiveBufferSize();
152           
153            if(x < REQUESTED_BUF_SIZE)
154                fprintf('OS reduced recv buffer size to %d\n', x);
155            end
156           
157            obj.rxBufferSize = x;
158            obj.status       = 1;
159        end
160       
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           
172            cmdStr = lower(cmdStr);
173            switch(cmdStr)
174           
175                %---------------------------------------------------------
176                case 'ping'
177                    % Test to make sure node can be accessed via this
178                    % transport
179                    %
180                    % Arguments: none
181                    % Returns: true if board responds; raises error otherwise
182                    %
183                    myCmd = wl_cmd(node.calcCmd(obj.GRP,obj.CMD_PING));
184                    node.sendCmd(myCmd);
185                    out = true; %sendCmd will timeout and raise error if board doesn't respond
186                   
187                %---------------------------------------------------------
188                case 'payload_size_test'
189                    % Determine objects maxPayload parameter
190                    %
191                    % Arguments: none
192                    % Returns: none
193                    %
194                    configFile = which('wl_config.ini');
195                   
196                    if(isempty(configFile))
197                       error('cannot find wl_config.ini. please run wl_setup.m'); 
198                    end
199                   
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});
203                   
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
211                    end
212                   
213                    payloadTestSizes = [payloadTestSizes, max_transport_payload_size];
214                   
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                   
221                    for index = 1:length(payloadTestSizes)
222                        myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_PAYLOADSIZETEST));
223                        myCmd.addArgs(1:payloadTestSizes(index));
224                        try
225                            resp = node.sendCmd(myCmd);
226                            obj.setMaxPayload( resp.getArgs );
227                            if(obj.getMaxPayload < (payloadTestSizes(index) * 4))
228                               break; 
229                            end
230                        catch ME
231                            break 
232                        end
233                    end
234                   
235                %---------------------------------------------------------
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
245                    %
246                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_ADD));
247                    myCmd.addArgs(varargin{1});
248                    node.sendCmd(myCmd);
249                   
250                %---------------------------------------------------------
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
260                    %
261                    myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODEGRPID_CLEAR));
262                    myCmd.addArgs(varargin{1});
263                    node.sendCmd(myCmd);
264                   
265                %---------------------------------------------------------
266                otherwise
267                    error('unknown command ''%s''',cmdStr);
268            end
269           
270            if((iscell(out) == 0) && (numel(out) ~= 1))
271                out = {out}; 
272            end     
273        end
274       
275        function close(obj)
276            if(~isempty(obj.sock))
277                try
278                    obj.sock.close();
279                catch closeError
280                    warning( 'Error closing socket; java error was %s', closeError.message)
281                end
282            end
283            obj.status=0;
284        end   
285
286        function delete(obj)
287            obj.close();
288        end
289
290        function flush(obj)
291            % Currently not implemented
292        end
293    end % methods
294
295    methods (Hidden = true)
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            %
302            import java.io.*
303            import java.net.DatagramSocket
304            import java.net.DatagramPacket
305
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};
318            end
319           
320            % Set the appropriate flags in the header           
321            if(robust)
322               obj.hdr.flags = bitset(obj.hdr.flags, 1, 1);
323            else
324               obj.hdr.flags = bitset(obj.hdr.flags, 1, 0);
325            end
326
327            % Increment the header and serialize all the data into a uint32 array
328            if (increment_hdr)
329                obj.hdr.increment;
330            end
331           
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
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
339
340            % Send the packet
341            obj.sock.send(pkt_send);
342           
343            % Wait to receive reply from the board               
344            if(robust == 1)
345                hdr_length       = obj.hdr.length;
346                pkt_recv         = obj.recv_pkt;
347                currTx           = 1;
348                numWaitRetries   = 0;
349                receivedResponse = 0;
350                currTime         = tic;
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'))
358                            % Timeout receiving; do nothing
359                            recv_len = 0;
360                        else
361                            fprintf('%s.m--Failed to receive UDP packet.\nJava error message follows:\n%s',mfilename,receiveError.message);
362                        end
363                    end
364                   
365                    % If we have a packet, then process the contents
366                    if(recv_len > 0)
367                        recv_data8 = pkt_recv.getData;
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 = [];
400                            end
401                        end
402                    end
403                   
404                    % Look for timeout
405                    if ((toc(currTime) > obj.timeout) && (receivedResponse == 0))
406                        if(currTx == maxAttempts)
407                            error('wl_transport_eth_java:send:noReply','maximum number of retransmissions met without reply from node'); 
408                        end
409                       
410                        % Retry the packet
411                        obj.sock.send(pkt_send);
412                        currTx   = currTx + 1;
413                        currTime = tic;
414                    end
415                end
416            end
417        end
418       
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       
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            %
444            import java.io.*
445            import java.net.DatagramSocket
446            import java.net.DatagramPacket
447
448            % Initialize variables
449            hdr_length  = obj.hdr.length;
450            pkt_recv    = obj.recv_pkt;
451            done        = false;
452            resp        = [];
453           
454            % Receive packets           
455            while ~done
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'))
461                        % Timeout receiving; do nothing
462                        recv_len = 0;
463                    else
464                        fprintf('%s.m--Failed to receive UDP packet.\nJava error message follows:\n%s',mfilename,receiveError.message);
465                    end
466                end
467               
468                % If we have a packet, then process the contents
469                if(recv_len > 0)
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                       
478                        if(isempty(reply))
479                            reply = [];
480                        end
481                       
482                        resp = [resp, reply];                       
483
484                        done = true;
485                    end
486                else
487                    done = true;
488                end
489            end
490        end
491       
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       
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);
525            dottedIPout  = sprintf('%d.%d.%d.%d', addrChars);
526        end
527
528        function intOut = IP2int(obj,dottedIP)
529            addrChars = sscanf(dottedIP, '%d.%d.%d.%d')';
530            intOut    = 2^0 * addrChars(4) + 2^8 * addrChars(3) + 2^16 * addrChars(2) + 2^24 * addrChars(1);
531        end
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
565    end
566end % classdef
Note: See TracBrowser for help on using the repository browser.