00001
00002
00003
00004
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:
00052 pass
00053
00054 class SlowAssoc:
00055 pass
00056
00057 class Reassociate:
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
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
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
00113 if not self.radio.associated.get():
00114 method = SlowAssoc
00115
00116 if method is None:
00117
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
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
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
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
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()