sta_test.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 
00003 ## This program does a test on an interface, as controlled by
00004 ## wpa_supplicant_node.
00005 
00006 import roslib; roslib.load_manifest('multi_interface_roam')
00007 import rospy
00008 import multi_interface_roam.radio as radio
00009 import random
00010 import itertools
00011 import time
00012 import multi_interface_roam.async_helpers as async_helpers
00013 import multi_interface_roam.mac_addr as mac_addr
00014 import multi_interface_roam.system as system
00015 import sys
00016 import os
00017 from twisted.internet import reactor
00018 from twisted.internet.defer import inlineCallbacks
00019 
00020 class SuccessCounter:
00021     def __init__(self, name):
00022         self.name = name
00023         self.successes = 0
00024         self.tries = 0
00025 
00026     def failure(self):
00027         self.count(False)
00028 
00029     def success(self):
00030         self.count(True)
00031 
00032     def count(self, cond):
00033         self.tries += 1
00034         if cond:
00035             self.successes += 1
00036 
00037     def __str__(self):
00038         if self.tries:
00039             percent = 100.0 * self.successes / self.tries
00040         else:
00041             percent = 0
00042         return "%s: %i/%i (%.1f%%)"%(self.name, self.successes, self.tries, percent)
00043 
00044 class Bss:
00045     def __init__(bssid, ssid, freq, ip):
00046         self.bssid = bssid
00047         self.ssid = ssid
00048         self.freq = freq
00049         self.ip = ip
00050 
00051 class FastAssoc: # Associate just after unassociating.
00052     pass
00053 
00054 class SlowAssoc: # Wait x seconds after unassociating.
00055     pass
00056 
00057 class Reassociate: # Do not request unassociation.
00058     pass
00059 
00060 class StaTester:
00061     def __init__(self, iface, bsses):
00062         self.bsses = bsses
00063         self.iface = iface
00064         self.radio = radio.Radio(iface)
00065         self.scan_counter = SuccessCounter('scn')
00066         self.mixed_scan_counter = SuccessCounter('mxsan')
00067         self.bgscan_counter = SuccessCounter('bgscn')
00068         self.ap_not_found_counter = SuccessCounter('AP_fnd')
00069         self.early_disassoc_counter = SuccessCounter("prsist")
00070         self.assoc_counters = { 
00071                 FastAssoc:   SuccessCounter("fst"), 
00072                 SlowAssoc:   SuccessCounter("slw"), 
00073                 Reassociate: SuccessCounter("rasc"),
00074                 }
00075         self.bss_counters = dict((bss['name'], SuccessCounter(bss['name'])) for bss in bsses)
00076         self.counters = self.assoc_counters.values() + \
00077                 [self.early_disassoc_counter, self.mixed_scan_counter, self.bgscan_counter, self.scan_counter, self.ap_not_found_counter] + \
00078                 self.bss_counters.values()
00079 
00080     @inlineCallbacks    
00081     def try_assoc(self, bss, method = None):
00082         # Already on this AP
00083         cur_assoc = self.radio.associated.get() 
00084         if cur_assoc:
00085             if mac_addr.same(cur_assoc.bssid, bss['bssid']):
00086                 print >> sys.stderr, "Already associated to", bss['name']
00087                 return
00088         del cur_assoc
00089 
00090         # Scan for this AP
00091         self.radio.scan([bss['freq']])
00092         pre_scan_assoc = self.radio.associated.get()
00093         print >> sys.stderr, "Scanning"
00094         scan_results = yield async_helpers.wait_for_event(self.radio.scan_results_event)
00095         post_scan_assoc = self.radio.associated.get()
00096         if pre_scan_assoc and post_scan_assoc:
00097             self.bgscan_counter.count(scan_results)
00098         elif not pre_scan_assoc and not post_scan_assoc:
00099             self.scan_counter.count(scan_results)
00100         else:
00101             self.mixed_scan_counter.count(scan_results)
00102         if not scan_results:
00103             print >> sys.stderr, "\nScan failed"
00104             return
00105         target_bss = filter(lambda cbss: mac_addr.same(cbss.bssid, bss['bssid']) and cbss.ssid == bss['ssid'], scan_results)
00106         self.ap_not_found_counter.count(target_bss)
00107         if not target_bss:
00108             print >> sys.stderr, "\nAP not found", bss['name']
00109             self.bss_counters[bss['name']].count(False)
00110             return
00111 
00112         # Decide which associate method to use
00113         if not self.radio.associated.get(): 
00114             method = SlowAssoc
00115 
00116         if method is None:
00117             # Randomly sample with priority to least sampled.
00118             r = random.random()
00119             universe = dict((m,c.tries) for m, c in self.assoc_counters.iteritems())
00120             most_visited = max(universe.itervalues())
00121             for m in universe:
00122                 universe[m] = 2 + max(universe[m] - most_visited, 0)
00123             total = float(sum(universe.values()))
00124             for method in universe:
00125                 r -= universe[method] / total
00126                 if r < 0:
00127                     break
00128 
00129         print >> sys.stderr, "Associate method", method
00130         
00131         if method != Reassociate:
00132             self.radio.unassociate()
00133             print >> sys.stderr, "Unassociating"
00134             yield async_helpers.wait_for_state(self.radio.associated, lambda x: x == radio.Unassociated)
00135 
00136         if method == SlowAssoc:
00137             print >> sys.stderr, "Waiting after unassociate"
00138             yield async_helpers.async_sleep(3)
00139 
00140         # Associate
00141         self.radio.associate((bss['ssid'], bss['bssid']))
00142         print >> sys.stderr, "Associating"
00143         yield async_helpers.wait_for_state(self.radio.associated, lambda x: x != radio.Associating)
00144 
00145         # Log success
00146         assoc_state = self.radio.associated.get()
00147         self.assoc_counters[method].count(assoc_state)
00148         self.bss_counters[bss['name']].count(assoc_state)
00149 
00150         if self.radio.associated.get():
00151             # If association succeeded, make sure it is stable.
00152             print >> sys.stderr, "Waiting after associating", bss['name'], method
00153             yield async_helpers.async_sleep(1)
00154             self.early_disassoc_counter.count(self.radio.associated.get())
00155         else:
00156             print >> sys.stderr, "\nAssoc failed", bss['name'], method
00157             system.system('iwconfig')
00158 
00159     @inlineCallbacks
00160     def run_test(self, method = None):
00161         while not rospy.is_shutdown():
00162             random.shuffle(self.bsses)
00163             for bss in self.bsses:
00164                 yield async_helpers.async_sleep(0.1)
00165                 yield self.try_assoc(bss, method)
00166                 #out = ["\r\033[2K"]
00167                 out = []
00168                 for counter in self.counters:
00169                     out.append(str(counter))
00170                 print >> sys.stderr, " ".join(out)
00171                 sys.stdout.flush()
00172 
00173 def shutdown_by_ros(why):
00174     reactor.fireSystemEvent('shutdown')
00175 
00176 def main():
00177     rospy.init_node("sta_tester")
00178     rospy.core.add_shutdown_hook(shutdown_by_ros)
00179 
00180     interface = rospy.get_param("~interface")
00181     bsses = rospy.get_param("~bsses")
00182     methodstr = rospy.get_param("~method", "None")
00183     try:
00184         method = {
00185                 "FastAssoc" : FastAssoc,
00186                 "SlowAssoc" : SlowAssoc,
00187                 "Reassociate" : Reassociate,
00188                 "None" : None,
00189                 }[methodstr]
00190     except:
00191         rospy.logerr("Bad ~method value: %s", methodstr)
00192     else:
00193         reactor.callWhenRunning(StaTester(interface, bsses).run_test, method)
00194         reactor.run()
00195 
00196 if __name__ == "__main__":
00197     main()


multi_interface_roam
Author(s): Blaise Gassend
autogenerated on Thu Jan 2 2014 11:26:15