1 | package 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 |
---|
13 | set ackSeqNum 1 |
---|
14 | set SendAck 1 |
---|
15 | set WriteStat 2 |
---|
16 | |
---|
17 | #User Specified Global Variables |
---|
18 | set filename "PacketData.m" |
---|
19 | set NumNodes 2; |
---|
20 | set dumbServerIP 10.0.0.6 |
---|
21 | set 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 |
---|
33 | proc 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 |
---|
139 | proc 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 |
---|
154 | proc 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 |
---|
202 | proc 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 |
---|
239 | proc 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 |
---|
253 | proc 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 |
---|
268 | proc 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 |
---|
288 | tsv::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 |
---|
291 | tsv::array set keyboardEnable {0 0} |
---|
292 | |
---|
293 | #Create a thread |
---|
294 | set 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 |
---|
318 | proc 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 |
---|
328 | eval [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 |
---|
335 | set ObserveStruct [list 49 0 2 30000 40000 50000 50000 50000 6000 700 10.3 ] |
---|
336 | |
---|
337 | ;# Control Struct: structType, nodeID, modOrder, txPower, coding, channel |
---|
338 | set ControlStruct0 [list 50 0 4 63 3 9 ] |
---|
339 | set ControlStruct1 [list 50 1 4 63 3 9 ] |
---|
340 | |
---|
341 | ;# Traffic Struct: structType, nodeID, trafficMode 0 = RECEIVE 1 = TRANSMIT , reserved0, txInterval, txPacketLen, reserved1 |
---|
342 | set TrafficStruct0 [list 52 0 1 0 0 1470 0 ] |
---|
343 | set TrafficStruct1 [list 52 1 1 0 0 1470 0 ] |
---|
344 | |
---|
345 | ;# structType, nodeID is only relevant for ack on stop, cmdID, cmdParam |
---|
346 | set CommandStructStart [list 51 0 102 0 ] |
---|
347 | set CommandStructStop [list 51 0 103 0 ] |
---|
348 | |
---|
349 | ;# structType, nodeID, cmdID, cmdParam |
---|
350 | set CommandStructStats0 [list 51 0 104 0 ] |
---|
351 | set CommandStructStats1 [list 51 1 104 0 ] |
---|
352 | set CommandStructResetStats0 [list 51 0 97 0 ] |
---|
353 | set CommandStructResetStats1 [list 51 1 97 0 ] |
---|
354 | |
---|
355 | |
---|
356 | ### MAIN PROGRAM ### |
---|
357 | |
---|
358 | #Open a socket connection |
---|
359 | set sock [socket $dumbServerIP $dumbServerPort] |
---|
360 | |
---|
361 | #Automate flushing of the socket |
---|
362 | fconfigure $sock -buffering none -blocking 0 |
---|
363 | |
---|
364 | set TimeInterval 100 |
---|
365 | ;#Create Blank File |
---|
366 | set fileID [open $filename "w"] |
---|
367 | |
---|
368 | #for {set p 0} {$p < 50} {incr p} { |
---|
369 | for {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 |
---|
373 | senddata $sock [list 50 0 [expra {$mod*2}] $power 3 9 ] $SendAck |
---|
374 | #Send a Control Struct to node 1 |
---|
375 | senddata $sock [list 50 1 [expr {$mod*2}] $power 3 9 ] $SendAck |
---|
376 | |
---|
377 | #Send a Traffic Struct to node 0 |
---|
378 | senddata $sock [list 52 0 1 0 0 1470 0 ] $SendAck |
---|
379 | #Send a Traffic Struct to node 1 |
---|
380 | senddata $sock [list 52 1 1 0 0 1470 0 ] $SendAck |
---|
381 | |
---|
382 | #Reset Statistics on nodes, sets everything to 0 |
---|
383 | senddata $sock $CommandStructResetStats0 $SendAck |
---|
384 | senddata $sock $CommandStructResetStats1 $SendAck |
---|
385 | |
---|
386 | #Begin data transmission over the air |
---|
387 | senddata $sock $CommandStructStart |
---|
388 | after $TimeInterval |
---|
389 | #Stop data tranmission over the air |
---|
390 | senddata $sock $CommandStructStop $SendAck |
---|
391 | |
---|
392 | senddata $sock $CommandStructStats0 $WriteStat |
---|
393 | senddata $sock $CommandStructStats1 $WriteStat |
---|
394 | |
---|
395 | } |
---|
396 | } |
---|
397 | #} |
---|
398 | |
---|
399 | |
---|
400 | close $sock |
---|
401 | exit |
---|