$search
00001 from __future__ import with_statement 00002 import radio 00003 from netlink_monitor import netlink_monitor, IFSTATE 00004 import async_helpers 00005 import asmach as smach 00006 from twisted.internet import reactor 00007 from twisted.internet.defer import inlineCallbacks, returnValue 00008 import state_publisher 00009 import operator 00010 import event 00011 00012 class RadioSmData: 00013 def __init__(self, iface_name): 00014 self._radio = radio.Radio(iface_name) 00015 self.iface_name = iface_name 00016 self._up_state_pub = netlink_monitor.get_state_publisher(iface_name, IFSTATE.UP) 00017 self.activate_request = state_publisher.StatePublisher(False) 00018 self.is_active = state_publisher.StatePublisher(False) 00019 self.scanning_enabled = state_publisher.StatePublisher(False) 00020 self.associate_request = event.Event() 00021 self.disactivate_delay = 1 00022 self.pre_up_sleep = 1 00023 00024 # Copy radio members that we are willing to expose 00025 self.unassociate = self._radio.unassociate 00026 self.associated = self._radio.associated 00027 self.scanning = self._radio.scanning 00028 self.scan = self._radio.scan 00029 self.scan_results_event = self._radio.scan_results_event 00030 self.frequency_list = self._radio.frequency_list 00031 00032 class RadioSmState(smach.State): 00033 def __init__(self, *args, **kwargs): 00034 smach.State.__init__(self, input_keys=['radio'], output_keys=['radio'], *args, **kwargs) 00035 00036 class Down(RadioSmState): 00037 def __init__(self): 00038 RadioSmState.__init__(self, outcomes=['up']) 00039 00040 @inlineCallbacks 00041 def execute_async(self, ud): 00042 # Wait a bit here because buggy drivers don't like to do things 00043 # just as they are coming down. 00044 yield async_helpers.async_sleep(ud.radio.pre_up_sleep) 00045 yield async_helpers.wait_for_state(ud.radio._up_state_pub, operator.truth) 00046 returnValue('up') 00047 00048 class Unassociated(RadioSmState): 00049 def __init__(self): 00050 RadioSmState.__init__(self, outcomes=['associate', 'down']) 00051 00052 @inlineCallbacks 00053 def execute_async(self, ud): 00054 # If we got here then were forcibly disactivated, and the previous 00055 # activation request is moot. 00056 ud.radio.activate_request.set(False) 00057 00058 # We may have landed here after the interface went down causing 00059 # disassociation. Quickly check for that before getting to work. 00060 if not ud.radio._up_state_pub.get(): 00061 returnValue('down') 00062 00063 # Use callLater so that no association request will arrive before 00064 # we select. 00065 reactor.callLater(0, ud.radio.scanning_enabled.set, True) 00066 assoc_event_stream = async_helpers.EventStream(ud.radio.associate_request) 00067 events = yield async_helpers.select( 00068 async_helpers.StateCondition(ud.radio._up_state_pub, operator.__not__ ), 00069 assoc_event_stream 00070 ) 00071 ud.radio.scanning_enabled.set(False) 00072 00073 if 1 in events: 00074 ud.radio.requested_bss = assoc_event_stream.get() 00075 returnValue('associate') 00076 returnValue('down') 00077 00078 class Associating(RadioSmState): 00079 def __init__(self): 00080 RadioSmState.__init__(self, outcomes=['associated', 'failed']) 00081 00082 @inlineCallbacks 00083 def execute_async(self, ud): 00084 ud.radio._radio.associate(*ud.radio.requested_bss[0], **ud.radio.requested_bss[1]) 00085 00086 #print "Associating before", ud.radio.associated.get() 00087 events = yield async_helpers.select( 00088 async_helpers.StateCondition(ud.radio._up_state_pub, operator.__not__ ), 00089 async_helpers.StateCondition(ud.radio._radio.associated, lambda x: x != radio.Associating)) 00090 00091 if 0 in events or ud.radio.associated.get() == radio.Unassociated: 00092 returnValue('failed') 00093 else: 00094 #print "Associated", ud.radio.iface_name, ud.radio.associated.get() 00095 returnValue('associated') 00096 00097 class Associated(RadioSmState): 00098 def __init__(self): 00099 RadioSmState.__init__(self, outcomes=['activate', 'reassociate', 'unassociated']) 00100 00101 @inlineCallbacks 00102 def execute_async(self, ud): 00103 # Use callLater so that no association request will arrive before 00104 # we select. 00105 reactor.callLater(0, ud.radio.scanning_enabled.set, True) 00106 assoc_event_stream = async_helpers.EventStream(ud.radio.associate_request) 00107 #print "Associated before", ud.radio.associated.get() 00108 events = yield async_helpers.select( 00109 async_helpers.StateCondition(ud.radio._up_state_pub, operator.__not__ ), 00110 async_helpers.StateCondition(ud.radio.activate_request, operator.truth), 00111 assoc_event_stream, 00112 ) 00113 ud.radio.scanning_enabled.set(False) 00114 00115 if 1 in events: 00116 returnValue('activate') 00117 if 2 in events: 00118 ud.radio.requested_bss = assoc_event_stream.get() 00119 #print "Assoc changed", ud.radio.iface_name, ud.radio.requested_bss 00120 returnValue('reassociate') 00121 returnValue('unassociated') 00122 00123 class Active(RadioSmState): 00124 def __init__(self): 00125 RadioSmState.__init__(self, outcomes=['disactivate', 'unassociated']) 00126 00127 @inlineCallbacks 00128 def execute_async(self, ud): 00129 events = yield async_helpers.select( 00130 async_helpers.StateCondition(ud.radio.activate_request, operator.__not__), 00131 async_helpers.StateCondition(ud.radio.associated, operator.__not__)) 00132 if 0 in events: 00133 returnValue('disactivate') 00134 returnValue('unassociated') 00135 00136 class Disactivating(RadioSmState): 00137 def __init__(self): 00138 RadioSmState.__init__(self, outcomes=['done', 'activate', 'unassociated']) 00139 00140 @inlineCallbacks 00141 def execute_async(self, ud): 00142 events = yield async_helpers.select( 00143 async_helpers.Timeout(ud.radio.disactivate_delay), 00144 async_helpers.StateCondition(ud.radio.activate_request, operator.truth), 00145 async_helpers.StateCondition(ud.radio.associated, operator.__not__)) 00146 if 1 in events: 00147 returnValue('activate') 00148 if 2 in events: 00149 returnValue('unassociated') 00150 returnValue('done') 00151 00152 def _state_change_cb(ud, states): 00153 ud.radio.is_active.set("ACTIVE" in states) 00154 00155 def radio_sm(iface): 00156 sm = smach.StateMachine(outcomes=[], input_keys=['radio']) 00157 smadd = smach.StateMachine.add 00158 with sm: 00159 smadd('DOWN', Down(), transitions = { 'up' : 'UNASSOCIATED' }) 00160 smadd('UNASSOCIATED', Unassociated(), transitions = { 'associate' : 'ASSOCIATING', 'down' : 'DOWN'}) 00161 smadd('ASSOCIATING', Associating(), transitions = { 'associated' : 'ASSOCIATED', 'failed' : 'DOWN'}) 00162 smadd('ASSOCIATED', Associated(), transitions = { 'reassociate' : 'ASSOCIATING', 'activate' : 'ACTIVE', 'unassociated' : 'DOWN'}) 00163 smadd('ACTIVE', Active(), transitions = { 'disactivate' : 'DISACTIVATING', 'unassociated' : 'DOWN'} ) 00164 smadd('DISACTIVATING', Disactivating(), transitions = { 'done' : 'ASSOCIATED', 'activate' : 'ACTIVE', 'unassociated' : 'DOWN'} ) 00165 ud = smach.UserData() 00166 ud.radio = RadioSmData(iface) 00167 sm.register_transition_cb(_state_change_cb) 00168 sm.execute_async(ud) 00169 return ud.radio