From: NeilBrown Date: Fri, 28 Dec 2012 06:22:35 +0000 (+1100) Subject: gsmd: change 'nextstate' to a queue X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=748bfc26b891dadf908c3d169f507c1fd7edb13d;p=plato.git gsmd: change 'nextstate' to a queue and various associated fixes. --- diff --git a/gsm/TOFIX b/gsm/TOFIX index bff8aba..5730f37 100644 --- a/gsm/TOFIX +++ b/gsm/TOFIX @@ -1,3 +1,46 @@ + +Fri Dec 28 16:29:37 2012 advance idle chooses 0, 29000 +Fri Dec 28 16:29:37 2012 Sleeping for 29.000000 seconds +Fri Dec 28 16:29:56 2012 received EOF +Fri Dec 28 16:29:56 2012 Force state to reset +Fri Dec 28 16:29:56 2012 advance reset chooses 0, 3600000 +Fri Dec 28 16:29:56 2012 Sleeping for 3600.000000 seconds +Traceback (most recent call last): + File "/usr/local/bin/atchan.py", line 72, in readdata + self.getline(None) + File "/usr/local/bin/atchan.py", line 90, in getline + self.takeline(line) + File "/usr/local/bin/gsmd", line 795, in takeline + self.advance() + File "/usr/local/bin/gsmd", line 778, in advance + self.set_timeout(delay) + File "/usr/local/bin/atchan.py", line 129, in set_timeout + raise ValueError +ValueError +Fri Dec 28 16:29:56 2012 received EOF +Fri Dec 28 16:29:56 2012 Force state to reset +Fri Dec 28 16:29:56 2012 advance reset chooses 0, 3600000 +Fri Dec 28 16:29:56 2012 Sleeping for 3600.000000 seconds +Traceback (most recent call last): + File "/usr/local/bin/atchan.py", line 72, in readdata + self.getline(None) + File "/usr/local/bin/atchan.py", line 90, in getline + self.takeline(line) + File "/usr/local/bin/gsmd", line 849, in takeline + self.main.takeline(line) + File "/usr/local/bin/gsmd", line 795, in takeline + self.advance() + File "/usr/local/bin/gsmd", line 778, in advance + self.set_timeout(delay) + File "/usr/local/bin/atchan.py", line 129, in set_timeout + raise ValueError +ValueError +Fri Dec 28 16:30:06 2012 Timer Fired +Fri Dec 28 16:30:06 2012 advance reset chooses 0, 3600000 +Fri Dec 28 16:30:06 2012 Sleeping for 3600.000000 seconds + + + Sun Dec 23 10:36:08 2012 send AT command +CPAS 2000 Sun Dec 23 10:36:08 2012 received AT response +CPAS: 4 Sun Dec 23 10:36:08 2012 call_status got +CPAS: 4 diff --git a/gsm/gsmd.py b/gsm/gsmd.py index 1d72506..06e1a0d 100644 --- a/gsm/gsmd.py +++ b/gsm/gsmd.py @@ -113,7 +113,7 @@ class AtAction(Task): def takeline(self, channel, line): if line == None: - channel.set_state('reset') + channel.force_state('reset') channel.advance() return m = self.ok.match(line) @@ -148,7 +148,7 @@ class AtAction(Task): def timeout(self, channel): if channel.state['retries'] >= 5: if self.critical: - channel.set_state('reset') + channel.force_state('reset') channel.advance() return channel.state['retries'] += 1 @@ -213,16 +213,13 @@ class ChangeStateAction(Task): def start(self, channel): if self.newstate: state = self.newstate - elif channel.statechanged: - state = channel.nextstate - channel.statechanged = False + elif channel.nextstate: + state = channel.nextstate.pop(0) else: state = None if state: channel.gstate = state channel.tasknum = None - if not channel.statechanged: - channel.nextstate = state log("ChangeStateAction chooses", channel.gstate) n = len(control[channel.gstate]) channel.lastrun = n * [0] @@ -431,9 +428,12 @@ def call_status(channel, line, m): control = {} # For flight mode, we turn the power off. -control['flight'] = [ +control['to-flight'] = [ AtAction(at='+CFUN=0'), PowerAction('off'), + ChangeStateAction('flight') +] +control['flight'] = [ BlockSuspendAction(False), ] @@ -447,8 +447,8 @@ control['reset'] = [ # For suspend, we want power on, but no wakups for status or cellid control['suspend'] = [ + AtAction(check='+CFUN?', ok='\+CFUN: [14]', at='+CFUN=1', timeout=10000), AtAction(check='+CPAS', ok='\+CPAS: (\d)', handle = call_status), - AtAction(check='+CFUN?', ok='\+CFUN: 1', at='+CFUN=1', timeout=10000), CheckSMS(), ChangeStateAction(None), # allow async state change AtAction(at='+CNMI=1,1,0,0,0'), @@ -460,6 +460,8 @@ control['suspend'] = [ ] control['resume'] = [ BlockSuspendAction(True), + AtAction(check='+CFUN?', ok='\+CFUN: [14]', at='+CFUN=1', timeout=10000), + AtAction(check='+CPAS', ok='\+CPAS: (\d)', handle = call_status), AtAction(at='+CNMI=1,1,2,0,0', critical=False), AtAction(at='_OSQI=1', critical=False), AtAction(at='+CREG=2'), @@ -520,7 +522,7 @@ control['idle'] = [ #AtAction(check='+COPS?', ok='\+COPS: \d+,\d+,"([^"]*)"', at='+COPS=0', # record=('carrier', '\\1'), timeout=10000, repeat=37000), # Make sure to use both 2G and 3G - AtAction(at='_OPSYS=3,2'), + AtAction(at='_OPSYS=3,2', critical=False), # get signal string AtAction(check='+CSQ', ok='\+CSQ: (\d+),(\d+)', record=('signal_strength','\\1/32'), repeat=29000) @@ -635,8 +637,7 @@ class GsmD(AtChannel): self.altpath = altpath self.altchan = CarrierDetect(altpath, self) self.gstate = None - self.nextstate = None - self.statechanged = False + self.nextstate = [] record('carrier','') record('cell','') @@ -667,7 +668,7 @@ class GsmD(AtChannel): l = recall('call') log("Check call got", l) if l == "": - if self.nextstate not in ['hangup', 'idle']: + if self.gstate != 'idle': global incoming_num incoming_num = None self.set_state('hangup') @@ -676,13 +677,13 @@ class GsmD(AtChannel): calllog_end('incoming') calllog_end('outgoing') elif l == 'answer': - if self.nextstate == 'incoming': + if self.gstate == 'incoming': record('status', 'on-call') record('incoming','') set_alert('ring', None) self.set_state('answer') else: - if self.nextstate == 'idle': + if self.gstate == 'idle': global calling calling = True self.args['number'] = l @@ -716,11 +717,11 @@ class GsmD(AtChannel): else: if not self.flightmode: self.flightmode = True - self.set_state('flight') + self.set_state('to-flight') def do_suspend(self): self.suspend_pending = True - if self.nextstate not in ['flight', 'resume']: + if self.gstate not in ['flight', 'resume']: print "do suspend sets suspend" self.set_state('suspend') else: @@ -729,18 +730,27 @@ class GsmD(AtChannel): return False def do_resume(self): - if self.nextstate == 'suspend': + if self.gstate == 'suspend': self.set_state('resume') def set_state(self, state): # this happens asynchronously so we must be careful # about changing things. Just record the new state # and abort any timeout + if state == self.gstate or state in self.nextstate: + log("state already destined to be", state) + return log("state should become", state) - self.nextstate = state - self.statechanged = True + self.nextstate.append(state) self.abort_timeout() + def force_state(self, state): + # immediately go to new state - must be called carefully + log("Force state to", state); + self.cancel_timeout() + self.nextstate = [] + self.gstate = state + def advance(self): # 'advance' is called by a 'Task' when it has finished # It may have called 'set_state' first either to report @@ -751,10 +761,9 @@ class GsmD(AtChannel): self.tasknum = None (t, delay) = self.next_cmd() log("advance %s chooses %d, %d" % (self.gstate, t, delay)) - if delay and self.statechanged: + if delay and self.nextstate: # time to effect 'set_state' synchronously - self.statechanged = False - self.gstate = self.nextstate + self.gstate = self.nextstate.pop(0) log("state becomes", self.gstate) n = len(control[self.gstate]) self.lastrun = n * [0] @@ -783,7 +792,7 @@ class GsmD(AtChannel): return False if line == None: - self.set_state('reset') + self.force_state('reset') self.advance() if not line: return False diff --git a/gsm/notes b/gsm/notes index dd77cea..9a9264d 100644 --- a/gsm/notes +++ b/gsm/notes @@ -113,4 +113,27 @@ We need to have some wake-from-suspend path that is less intense. sub states. That is bad. - resume is immediately replaced by 'suspend' which hangs around longer than we want... I guess the cpas and cfun and checksms should protect - against staying in suspend... but how? \ No newline at end of file + against staying in suspend... but how? + + +I need to queue state change requests so no state misses out. +i.e. I don't want 'resume' to be immediately replaced by 'incoming' before it +has a chance to do anything. +But I sometimes check the 'next' state when responding to 'async' actions. +What does that mean when there is a queue of 'next' states? + +check_call: if EMPTY not hangup or idle, - enter hangup + if ANSWER and is 'incoming' - enter 'on-call' + if NUMBER and is 'idle' - enter 'call' + This can be gstate +do_suspend: if not 'flight' or 'resume', enter 'suspend' + This can be gstate +do_resume: if 'suspend' become 'resume' + This can be just 'gstate' + +We sometimes check gstate directly: + incoming: if not on-call, incoming, answer : set to incoming + no_carrier: if not 'idle', set to 'idle' + call_status0: if 'on-call, incoming, call, clear call + if not idle or suspend, become idle + call_status3: if not 'incoming' or 'answer', become 'incoming'