OFDMReferenceDesign/Applications/Characterization: interactive_client.tcl

File interactive_client.tcl, 11.1 KB (added by rpl1, 15 years ago)
Line 
1package require Thread
2
3##
4#@file automated_client.tcl
5#
6#@author Richard Latimer
7#
8#@brief Includes functions for automation for sending warpnode structs and receiving structs with acks.
9#In addition this write stat structs to a file. The code connects with the dumb_server over port 9090
10
11
12#Set Global Variables
13set ackSeqNum 1
14set SendAck 1
15set WriteStat 2
16set filename "PacketData.m"
17set NumNodes 2;
18set dumbServerIP 10.0.0.6
19set dumbServerPort 9090
20
21##
22#Sends data over socket, adding on the sequence number and number to specify it is a tcl string for
23#the dumb_server. The function can confirm acks or stat packets which are formatted for matlab by
24#the dumb_server.
25#
26#@param chan, the socket ID
27#
28#@param data, the string to be sent over the socket
29#
30#@param {arg}, arguement for the type of feedback to receive. Default=0 request nothing, SendAck=1 request acks,  WriteStat=2 request Stats and write to a file
31proc senddata {chan data {arg 0}} {
32
33    #Specify global variables usd by the function
34    global ackSeqNum
35    global SendAck
36    global WriteStat
37    global NumNodes
38   
39
40    #Request and ack packet from dumb_server
41    if {$arg == $SendAck} {
42   
43        #Package data to be transmitted to dumb_server
44        set val "1 $ackSeqNum $data"
45       
46        #Send $val over socket $chan
47        puts $chan $val
48        puts "Sent: $val"
49       
50        #Sleep 1 millsecond to help aid the cleaning of the buffer
51        after 1
52   
53        set attempts 1
54        #Check for an ack handshake, otherwise retransmit
55        while {[ackHandshake $chan]} {
56            incr attempts
57            puts "Attempted transmission: $attempts"
58           
59            #Number of times to retransmit data before giving up
60            if {$attempts == 5} {
61                puts "Could not get ack"
62                close $chan
63                exit
64            }   
65           
66            #Resend $val over socket $chan
67            puts $chan $val
68           
69            #Sleep 1 millsecond to help aid the cleaning of the buffer
70            after 1
71        }
72       
73        incr ackSeqNum
74       
75        #seqNum on server and Nodes is a char with a max value of 256
76        if {$ackSeqNum==256} {
77            set ackSeqNum 1
78        }
79        return
80    }
81   
82    #Request statistics from dumb_server
83    if {$arg == $WriteStat} {
84        #Package data to be transmitted to dumb_server
85        set val "1 0 $data"
86       
87        #Send $val over socket $chan
88        puts $chan $val
89        puts "Sent: $val"
90       
91
92        set attempts 1
93       
94        #Read statics and write to a file for the number of $NumNodes
95        for {set i 0} { $i < $NumNodes } {incr i} {
96            while {[statHandshake $chan]} {
97                incr attempts
98                puts "Attempted transmission: $attempts"
99               
100                #Number of times to retransmit data before giving up
101                if {$attempts == 5} {
102                    puts "Could not get stat"
103                    close $chan
104                    exit
105                }
106               
107                #Resend $val over socket $chan
108                puts $chan $val
109               
110                #Sleep 1 millsecond to help aid the cleaning of the buffer
111                after 1
112
113            }
114        }
115        return
116 
117    }
118   
119    #Default: Send data without expecting anything in return
120   
121    set val "1 0 $data"
122    #Send $val over socket $chan
123    puts $chan $val
124    puts "Sent: $val"
125   
126    #Sleep 1 millsecond to help aid the cleaning of the buffer
127    after 1
128   
129}
130
131##
132#Read data from socket.
133#
134#@param chan, the channel socket ID
135#
136#@return data from socket
137proc receivedata {chan} {
138    set received [gets $chan]                 ;# Send a string
139    ;#puts "Received: $received"               
140    after 1
141    return $received
142}
143
144##
145#Reads the socket for the parameter that signifies an ack from server.
146#This function checks for one second if the socket has an ack and if
147#the ack contains the proper seqNum.
148#
149#@param chan, the socket ID
150#
151#@return 1 if the correct ack and seqNum ARE NOT received, 0 if they ARE received
152proc ackHandshake {chan} {
153
154    #Specify global variables usd by the function
155    global ackSeqNum
156    set i 0
157   
158    while {1} {
159   
160        #Read data from channel socket
161        set data [receivedata $chan]
162        #Split data by whitespaces into a list of values
163        set dataList [regexp -inline -all -- {\S+} $data]
164       
165        #Check if the second to last value is "_ack"
166        if {[lindex $dataList  end-1] == "_ack_"} {
167            ;#puts "Received Ack with value: $data"
168            ;#puts "Delay $i msec" ;#Delay between each command
169           
170            #Check if the last value, which is the seqNum, matches the $ackSeqNum
171            if {[lindex $dataList end] == $ackSeqNum} {
172                ;#puts "SeqNum Matches" ;#If we receive and ack and the SeqNum is the same
173                return 0       
174            }
175            #If an _ack_ was received at precisely 1000 milliseconds, but contained the wrong seqNum
176            if {$i==1000} {
177                puts "SeqNum MisMatch: Expected $ackSeqNum but got [lindex $dataList end]"
178                return 1   
179            }
180           
181        }
182        #If approximately 1000 millseconds pass without receiving an ack
183        if {$i==1000} {
184            puts "Didn't receive ACK"
185            return 1
186        }
187       
188        after 1
189        incr i
190    }
191}
192
193##
194#Reads the socket for the string signifying statistics from the server. It then writes
195#the data to the file specified globally as $filename
196#
197#@param chan, the socket ID
198#
199#@return 1 if the statistic data IS NOT received, 0 if IS received
200proc statHandshake {chan} {
201    set i 0
202    while {1} {
203   
204        #Read data from channel socket
205        set data [receivedata $chan]
206        #Read only values that are relevant to statistics data
207        set data2 [regexp -inline -all -- {Node.*_statAck_} $data]
208        #Split data by whitespaces into a list of values
209        set dataList [regexp -inline -all -- {\S+} [join $data2]]
210       
211        #Check if the last value of data list signifies statistic data
212        if {[lindex $dataList  end] == "_statAck_"} {
213            puts "Received/Writing StatAck with value: $data"
214            puts "Delay $i"
215
216            #Write statistic to file, while removing indicators that signify the data is statistic data
217            writeStats [join [lreplace $dataList end end "\n"] " "] ;#Remove \n and _statAck_ from list, then make the list a string
218                return 0       
219        }
220       
221        #If approximately 1000 millseconds pass without receiving an ack
222        if {$i==1000} {
223            puts "Didn't receive Stat Packet"
224            return 1
225        }
226       
227        after 1
228        incr i
229    }
230}
231
232##
233#Appends data to the file specified by the global variable $filename. Attaches a carriage return
234#after writing data
235#
236#@param data, the data to be written to file $filename
237proc write {data} {
238    #Specify global variables usd by the function
239    global filename
240    set fileID [open $filename "a"]
241    puts -nonewline $fileID $data
242    puts -nonewline $fileID "\n"
243    close $fileID
244}
245
246##
247#Appends statistic data to the file specified by the global variable $filename.
248#Attaches a carriage return after writing data. The prints to the command line the statistics
249#
250#@param data, the data to be written to file $filename
251proc writeStats {data} {
252    #Specify global variables usd by the function
253    global filename
254    set fileID [open $filename "a"]
255    puts -nonewline $fileID $data
256    puts -nonewline $fileID "\n"
257    close $fileID
258   
259    printStats $data
260}
261
262##
263#Prints to the command line the statistics data
264#
265#@param data, the statistics data to be printed
266proc printStats {data} {
267    set dataList [regexp -inline -all -- {\S+} $data]
268    puts "\n"
269    puts "Stat Struct Contents:"
270    puts "\tNode: [lindex $dataList 0]"
271    puts "\tSec: [lindex $dataList 2]"
272    puts "\tUsec: [lindex $dataList 3]"
273    puts "\tGoodPackets: [lindex $dataList 4]"
274    puts "\tOtherBadPackets: [lindex $dataList 5]"
275    puts "\tPartnerBadPackets: [lindex $dataList 6]"
276    puts "\tRxBytes: [lindex $dataList 7]"
277    puts "\tTxBytes: [lindex $dataList 8]"
278    puts "\tTimeInterval: [lindex $dataList 9]"
279    puts "\tThroughput: [lindex $dataList 10]\n"
280
281}
282
283###THREADED FUNCTIONS AND VALUES###
284
285#Keyboard input value shared among threads
286tsv::array set keyboardValue {0 0} ;#Arrays must have an even number of variables, The first argument states
287                                   ;#Which value in the array is assigned, the next number is the stored value
288#Keyboard enable shared among threads
289tsv::array set keyboardEnable {0 0}
290
291
292#Create a thread
293set threadID [thread::create {
294   
295    ##
296    #Reads the keyboard if $keyboardEnable is 1, write the keyboard value to $keyboardValue, then
297    #set $keyboardEnable to 0
298     proc keyboardInputthread {} {
299        global val
300        while {1} {
301        if {[eval tsv::get keyboardEnable 0]  == 1} {
302            puts "Input Command: "
303            tsv::set keyboardValue 0 [gets stdin]
304            tsv::set keyboardEnable 0 0
305            }
306        }
307    }
308   
309    #Keep thread alive
310    ::thread::wait
311}]
312
313##
314#Enable keyboard input and read keyboard input
315#
316#@return keyboardValue, the value inputted into the keyboard
317proc keyboardInput {} {
318    tsv::set keyboardEnable 0 1
319    while {1} {
320        if {[tsv::get keyboardEnable 0]  == 0} {
321            return [eval tsv::get keyboardValue 0] ;#Returns keyboard input
322        }
323    }
324}
325
326#Evaluate thread
327eval [subst {thread::send -async $threadID {keyboardInputthread } }]
328
329
330### EXAMPLES OF STRUCTURES###
331
332;# Observe Struct:  structType, nodeID, partnerID, Time sec, time usec, goodPackets, partnerBadPackets, rxBytes, txBytes, otherBadPackets, throughput
333set ObserveStruct [list 49 0 2 30000 40000 50000 50000 50000 6000 700 10.3 ]
334
335;# Control Struct: structType, nodeID, modOrder, txPower, coding, channel
336set ControlStruct0 [list 50 0 4 63 3 9 ]
337set ControlStruct1 [list 50 1 4 63 3 9 ]
338
339;# Traffic Struct: structType, nodeID, trafficMode 0 = receive 1= transmit , reserved0, txInterval, txPacketLen, reserved1
340set TrafficStruct0 [list 52 0 1 0 0 1470 0 ]
341set TrafficStruct1 [list 52 1 1 0 0 1470 0 ]
342
343;# structType, nodeID is only relevant for ack on stop, cmdID, cmdParam
344set CommandStructStart [list 51 0 102 0 ]
345set CommandStructStop [list 51 0 103 0 ]
346
347;# structType, nodeID, cmdID, cmdParam
348set CommandStructStats0 [list 51 0 104 0 ]
349set CommandStructStats1 [list 51 1 104 0 ]
350set CommandStructResetStats0 [list 51 0 97 0 ]
351set CommandStructResetStats1 [list 51 1 97 0 ]
352
353
354
355### MAIN PROGRAM ###
356
357set sock [socket 10.0.0.6 9090]         ;# Open the connection
358fconfigure $sock -buffering none -blocking 0       ;# automate flushing
359
360set TimeInterval 0 ;#Time in milliseconds
361set fileID [open $filename "w"] ;#Clear file if it exists
362
363for {set power 62} {$power < 64} {incr power} {
364    for {set mod 2} {$mod<3} {incr mod} { ;#set mod
365   
366   
367senddata $sock [list 50 0 [expr {$mod*2}] $power 3 9 ] $SendAck ;#Control Structs
368senddata $sock [list 50 1 [expr {$mod*2}] $power 3 9 ] $SendAck
369senddata $sock [list 52 0 1 0 0 1470 0 ] $SendAck ;#Traffic Struct
370senddata $sock [list 52 1 1 0 0 1470 0 ] $SendAck
371
372senddata $sock $CommandStructResetStats0 $SendAck
373senddata $sock $CommandStructResetStats1 $SendAck
374
375puts "Type a number great than 0 to set a time for test in msecs"
376puts "Press Enter to run test for an unspecified time"
377set xtime [eval keyboardInput]
378senddata $sock $CommandStructStart
379if {$xtime > 0} {
380    set TimeInterval $xtime} {
381    puts "Press Enter when ready to stop test"
382    set TimeInterval 0
383    keyboardInput
384    }
385
386
387
388after $TimeInterval
389       
390senddata $sock $CommandStructStop $SendAck
391senddata $sock $CommandStructStats0 $WriteStat
392senddata $sock $CommandStructStats1 $WriteStat
393   
394    }
395}
396   
397
398close $sock
399exit