]> git.neil.brown.name Git - plato.git/commitdiff
gsmd: change 'nextstate' to a queue
authorNeilBrown <neilb@suse.de>
Fri, 28 Dec 2012 06:22:35 +0000 (17:22 +1100)
committerNeilBrown <neilb@suse.de>
Fri, 28 Dec 2012 06:22:35 +0000 (17:22 +1100)
and various associated fixes.

gsm/TOFIX
gsm/gsmd.py
gsm/notes

index bff8abaf6c6f798e2141b04fa138c777e845995d..5730f3740ee4f7d74e9cf81d44ac8969359a2e6f 100644 (file)
--- 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
index 1d7250688dac0d1aba8e2e3afb3db959b13e655b..06e1a0d3e5cfc0411cf1f2917b113fe66615ea0d 100644 (file)
@@ -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
index dd77cea4e32f5b98c062e7e7923657b3ec010839..9a9264d515e420b8ae11c038951e5b4714c465fb 100644 (file)
--- 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'