radio.py
Go to the documentation of this file.
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             # assume it is a message
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         #def debug_assoc(iface, old_state, new_state):
00057         #    print "Assoc change", iface, new_state
00058         #self.associated.subscribe(debug_assoc, interface_name)
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         #print "scan goal", goal
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         # DANGER, this is not run in the main thread!!
00113         
00114         print "_assoc_done_cb", self.interface_name
00115         # Make sure that no feedback for this goal will arrive after we
00116         # have processed the goal.
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() # The filtered scan results are out of date.
00139 
00140     def enabled_bss(self, bss):
00141         for net in self.networks:
00142             # @todo This check will not work for <any> netwoks. FIXME
00143             # @todo Might want to check that security parameters match too. FIXME
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 


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