00001 from __future__ import with_statement
00002 import roslib; roslib.load_manifest("multi_interface_roam")
00003 import rospy
00004 import wpa_supplicant_node.msg as wpa_msgs
00005 import actionlib
00006 import state_publisher
00007 import mac_addr
00008 import event
00009 import threading
00010 from twisted.internet.defer import Deferred
00011 from twisted.internet import reactor
00012 from async_helpers import mainThreadCallback
00013
00014 class Network:
00015 def __init__(self, id, enabled, parameters):
00016 self.id = id
00017 self.enabled = enabled
00018 if type(parameters) != dict:
00019
00020 parameters = dict((p.key, p.value) for p in parameters)
00021 self.parameters = parameters
00022 self.__getitem__ = parameters.__getitem__
00023 try:
00024 self.ssid = self['ssid'][1:-1]
00025 except KeyError:
00026 self.ssid = None
00027
00028 class AssociationState(type):
00029 def __new__(meta, classname, bases, classDict):
00030 return type.__new__(meta, classname, bases, classDict)
00031 def __nonzero__(cls):
00032 return False
00033
00034 class Associating(object):
00035 __metaclass__ = AssociationState
00036
00037 class Unassociated(object):
00038 __metaclass__ = AssociationState
00039
00040 def is_not_associating(state):
00041 print state, id(state), id(Associating)
00042 return state != Associating
00043
00044 class Radio:
00045 def __init__(self, interface_name):
00046 self.interface_name = interface_name
00047 self._last_assoc_msg = None
00048 self._associating = False
00049 self.associated = state_publisher.StatePublisher(Unassociated)
00050 self.frequency_list = state_publisher.StatePublisher([])
00051 self.network_list = state_publisher.StatePublisher([])
00052 self.scan_results_event = event.Event()
00053 self.scanning = state_publisher.StatePublisher(False)
00054 self.mutex = threading.Lock()
00055
00056
00057
00058
00059
00060 self.networks = []
00061 self.hidden_networks = []
00062 self.raw_scan_results = []
00063 self.scan_results = []
00064 self.scan_deferred = None
00065
00066 prefix = rospy.resolve_name("wifi")+"/"+interface_name+"/"
00067 rospy.Subscriber(prefix + "scan_results", wpa_msgs.ScanResult, self._scan_results_callback)
00068 rospy.Subscriber(prefix + "network_list", wpa_msgs.NetworkList, self._network_list_callback)
00069 rospy.Subscriber(prefix + "frequency_list", wpa_msgs.FrequencyList, self._frequency_list_callback)
00070 self._scan_action = actionlib.SimpleActionClient(prefix+"scan", wpa_msgs.ScanAction)
00071 self._associate_action = actionlib.SimpleActionClient(prefix+"associate", wpa_msgs.AssociateAction)
00072
00073 def is_scanning(self):
00074 return _scan_action.simple_state == SimpleGoalState.DONE
00075
00076 def scan(self, freqs = [], ssids = []):
00077 goal = wpa_msgs.ScanGoal()
00078 for s in ssids:
00079 goal.ssids.append(s)
00080 for f in freqs:
00081 goal.frequencies.append(f)
00082
00083 self._scan_action.send_goal(goal, done_cb = self._scan_done_callback)
00084 self.scanning.set(True)
00085 self.scan_deferred = Deferred()
00086 return self.scan_deferred
00087
00088 def cancel_scans(self):
00089 self.scanning = False
00090 _scan_action.cancel_all_goals()
00091
00092 def associate(self, id):
00093 print "radio.associate", self.interface_name
00094 ssid = id[0]
00095 bssid = mac_addr.to_packed(id[1])
00096 with self.mutex:
00097 self._associate_action.send_goal(wpa_msgs.AssociateGoal(ssid, bssid),
00098 done_cb = self._assoc_done_cb,
00099 feedback_cb = self._assoc_feedback_cb)
00100 self.associated.set(Associating)
00101
00102 def unassociate(self):
00103 self._associate_action.cancel_all_goals()
00104
00105 @mainThreadCallback
00106 def _assoc_feedback_cb(self, fbk):
00107 print "_assoc_feedback_cb", self.interface_name, fbk.associated
00108 if fbk.associated:
00109 self.associated.set(fbk.bss)
00110
00111 def _assoc_done_cb(self, state, rslt):
00112
00113
00114 print "_assoc_done_cb", self.interface_name
00115
00116
00117 with self.mutex:
00118 self._associate_action.stop_tracking_goal()
00119 reactor.callFromThread(self.associated.set, Unassociated)
00120
00121 @mainThreadCallback
00122 def _scan_done_callback(self, state, rslt):
00123 """Called when all our scans are done."""
00124 self.scanning.set(False)
00125 if self.scan_deferred:
00126 self.scan_deferred.callback(rslt.success)
00127 self.scan_deferred = None
00128
00129 @mainThreadCallback
00130 def _frequency_list_callback(self, msg):
00131 self.frequency_list.set(msg.frequencies)
00132
00133 @mainThreadCallback
00134 def _network_list_callback(self, msg):
00135 self.networks = [ Network(net.network_id, net.enabled, net.parameters) for net in msg.networks ]
00136 self.hidden_networks = [ net for net in self.networks if net.parameters['scan_ssid'] == '1' ]
00137 self.network_list.set(self.networks)
00138 self._filter_raw_scan_results()
00139
00140 def enabled_bss(self, bss):
00141 for net in self.networks:
00142
00143
00144 if net.ssid == bss.ssid:
00145 return True
00146 return False
00147
00148 def _filter_raw_scan_results(self):
00149 self.scan_results = [ bss for bss in self.raw_scan_results if self.enabled_bss(bss) ]
00150 self.scan_results = filter(lambda bss: bss.frequency in self.frequency_list.get(), self.scan_results)
00151 self.scan_results_event.trigger(self.scan_results)
00152
00153 @mainThreadCallback
00154 def _scan_results_callback(self, msg):
00155 self.raw_scan_results = msg.bss
00156 self._filter_raw_scan_results()
00157