$search
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()