OFDMReferenceDesign/Applications/Characterization: automated_client.tcl

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