From b0c89a2c388412848923d2eb5f6058b2c9ea5131 Mon Sep 17 00:00:00 2001
From: Bernard Blackham <dagobah@ucc.asn.au>
Date: Sat, 7 May 2005 12:01:11 +0000
Subject: [PATCH] And here's a reworking of the idler logic.

---
 sql-edition/servers/VendServer.py     | 114 +++++++++-----------------
 sql-edition/servers/VendingMachine.py |   1 +
 2 files changed, 40 insertions(+), 75 deletions(-)

diff --git a/sql-edition/servers/VendServer.py b/sql-edition/servers/VendServer.py
index 22958bd..dd2cd7d 100755
--- a/sql-edition/servers/VendServer.py
+++ b/sql-edition/servers/VendServer.py
@@ -16,7 +16,7 @@ from VendingMachine import VendingMachine, VendingException
 from MessageKeeper import MessageKeeper
 from HorizScroll import HorizScroll
 from random import random, seed
-from Idler import TrainIdler,GrayIdler,StringIdler,ClockIdler,FortuneIdler,FileIdler,PipeIdler
+from Idler import GreetingIdler,TrainIdler,GrayIdler,StringIdler,ClockIdler,FortuneIdler,FileIdler,PipeIdler
 import socket
 from posix import geteuid
 
@@ -36,7 +36,6 @@ For a good time call +61 8 6488 3901
 
 """
 
-GREETING = 'UCC SNACKS'
 PIN_LENGTH = 4
 
 DOOR = 1
@@ -54,7 +53,7 @@ STATE_GET_SELECTION = 6
 STATE_GRANDFATHER_CLOCK = 7
 
 TEXT_SPEED = 0.8
-IDLE_SPEED = 0.02
+IDLE_SPEED = 0.05
 
 class DispenseDatabaseException(Exception): pass
 
@@ -213,31 +212,43 @@ def setup_idlers(v):
 		]
 	disabled = [
 		]
-	idler = choose_idler()
+
+def reset_idler(v, vstatus, t = None):
+	global idlers, idler
+	idler = GreetingIdler(v, t)
+	vstatus.time_of_next_idlestep = time()+idler.next()
+	vstatus.time_of_next_idler = None
+	vstatus.change_state(STATE_IDLE, 1)
 
 def choose_idler():
 	global idlers, idler
 	iiindex = 0
+	average_affinity = 10 # guessing here...
 
-	if idler:
+	if idler and idler.__class__ != GreetingIdler:
 		iiindex = idlers.index(idler)
 
 	iilen = len(idlers)
 
-	move = int(random()*len(idlers)) + 1
+	move = int(random()*len(idlers)*average_affinity) + 1
 
 	while move >= 0:
-		idler = idlers[( (iiindex + 1) % iilen)]
-		move = move - idler.affinity()
+		iiindex += 1
+		iiindex %= iilen
+		idler = idlers[iiindex]
+		move -= idler.affinity()
 
 	idler.reset()
 
-def idle_step():
+def idle_step(vstatus):
 	global idler
 	if idler.finished():
 		choose_idler()
-	sleep(IDLE_SPEED)
-	idler.next()
+		vstatus.time_of_next_idler = time() + 30
+	nextidle = idler.next()
+	if nextidle is None:
+		nextidle = IDLE_SPEED
+	vstatus.time_of_next_idlestep = time()+nextidle
 
 class VendState:
 	def __init__(self,v):
@@ -252,8 +263,6 @@ class VendState:
 		self.cur_selection = ''
 		self.time_to_autologout = None
 
-		self.time_to_idle = None
-
 		self.last_timeout_refresh = None
 
 	def change_state(self,newstate,newcounter=None):
@@ -311,10 +320,7 @@ def handle_get_selection_idle(state, event, params, v, vstatus):
 		vstatus.cur_pin = ''
 		vstatus.cur_selection = ''
 			
-		idle_in(vstatus,2)
-		vstatus.change_state(STATE_IDLE)
-
-		vstatus.mk.set_message(GREETING)
+		reset_idler(v, vstatus)
 
 	### State fully logged out ... reset variables
 	if vstatus.time_to_autologout and not vstatus.mk.done(): 
@@ -328,12 +334,6 @@ def handle_get_selection_idle(state, event, params, v, vstatus):
 		vstatus.time_to_autologout = time() + 15
 		vstatus.last_timeout_refresh = None
 
-	### State logged out ... after normal logout??
-	# perhaps when logged in?
-	if vstatus.time_to_idle is not None and vstatus.cur_user != '': 
-		vstatus.time_to_idle = None
-
-
 	## FIXME - this may need to be elsewhere.....
 	# need to check
 	vstatus.mk.update_display()
@@ -348,12 +348,9 @@ def handle_get_selection_key(state, event, params, v, vstatus):
 			vstatus.cur_user = ''
 			vstatus.cur_selection = ''
 			
-			idle_in(vstatus,2)
-			vstatus.change_state(STATE_IDLE)
+			reset_idler(v, vstatus)
 
-			vstatus.mk.set_messages(
-				[(center('BYE!'), False, 1.5),
-				 (GREETING, False, None)])
+			vstatus.mk.set_messages([(center('BYE!'), False, 1.5)])
 			return
 		vstatus.cur_selection += chr(key + ord('0'))
 		vstatus.mk.set_message('SELECT: '+vstatus.cur_selection)
@@ -416,10 +413,7 @@ def handle_getting_pin_key(state, event, params, v, vstatus):
 		if key == 11:
 			if vstatus.cur_pin == '':
 				vstatus.cur_user = ''
-				vstatus.mk.set_message(GREETING)
-			
-				idle_in(vstatus,5)
-				vstatus.change_state(STATE_IDLE)
+				reset_idler(v, vstatus)
 
 				return
 			vstatus.cur_pin = ''
@@ -439,13 +433,11 @@ def handle_getting_pin_key(state, event, params, v, vstatus):
 				v.beep(40, False)
 				vstatus.mk.set_messages(
 					[(center('BAD PIN'), False, 1.0),
-					 (center('SORRY'), False, 0.5),
-					 (GREETING, False, None)])
+					 (center('SORRY'), False, 0.5)])
 				vstatus.cur_user = ''
 				vstatus.cur_pin = ''
 			
-				idle_in(vstatus,5)
-				vstatus.change_state(STATE_IDLE)
+				reset_idler(v, vstatus, 2)
 
 				return
 
@@ -457,10 +449,8 @@ def handle_getting_uid_key(state, event, params, v, vstatus):
 	if len(vstatus.cur_user) < 5:
 		if key == 11:
 			vstatus.cur_user = ''
-			vstatus.mk.set_message(GREETING)
 
-			idle_in(vstatus,5)
-			vstatus.change_state(STATE_IDLE)
+			reset_idler(v, vstatus)
 			return
 
 		vstatus.cur_user += chr(key + ord('0'))
@@ -471,13 +461,11 @@ def handle_getting_uid_key(state, event, params, v, vstatus):
 		if not has_good_pin(uid):
 			logging.info('user '+vstatus.cur_user+' has a bad PIN')
 			vstatus.mk.set_messages(
-				[(' '*10+'INVALID PIN SETUP'+' '*10, False, 3),
-				 (GREETING, False, None)])
+				[(' '*10+'INVALID PIN SETUP'+' '*10, False, 3)])
 			vstatus.cur_user = ''
 			vstatus.cur_pin = ''
 			
-			idle_in(vstatus,5)
-			vstatus.change_state(STATE_IDLE)
+			reset_idler(v, vstatus, 5)
 
 			return
 
@@ -496,9 +484,7 @@ def handle_idle_key(state, event, params, v, vstatus):
 
 	if key == 11:
 		vstatus.cur_user = ''
-		vstatus.mk.set_message(GREETING)
-		idle_in(vstatus,5)
-		choose_idler()
+		reset_idler(v, vstatus)
 		return
 	
 	vstatus.change_state(STATE_GETTING_UID)
@@ -506,18 +492,11 @@ def handle_idle_key(state, event, params, v, vstatus):
 
 
 def handle_idle_tick(state, event, params, v, vstatus):
-	### State logged out ... initiate idler in 5  (first start?)
-	if vstatus.time_to_idle == None and vstatus.cur_user == '':
-		vstatus.time_to_idle = time() + 5
-		choose_idler()
-
 	### State idling
+	idle_step(vstatus)
 
-	if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
-		idle_step()
-
-	if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle + 30:
-		vstatus.time_to_idle = time()
+	if vstatus.time_of_next_idler and time() > vstatus.time_of_next_idler:
+		vstatus.time_of_next_idler = time() + 30
 		choose_idler()
 	
 	###
@@ -637,8 +616,6 @@ def handle_door_idle(state, event, params, v, vstatus):
 	pass
 
 def handle_door_event(state, event, params, v, vstatus):
-	vstatus.time_to_idle = None
-
 	if params == 1:  #door open
 		vstatus.change_state(STATE_DOOR_OPENING)
 		logging.warning("Entering open door mode")
@@ -648,23 +625,13 @@ def handle_door_event(state, event, params, v, vstatus):
 		vstatus.cur_pin = ''
 	elif params == 0:  #door closed
 		vstatus.change_state(STATE_DOOR_CLOSING)
-		idle_in(vstatus, 5)
+		reset_idler(v, vstatus, 3)
 
 		logging.warning('Leaving open door mode')
 		v.display("-YUM YUM!-")
 
-def idle_in(vstatus,seconds):
-	vstatus.time_to_idle = time() + seconds
-
 def return_to_idle(state,event,params,v,vstatus):
-	if vstatus.time_to_idle is not None and time() > vstatus.time_to_idle: 
-		vstatus.mk.set_message(GREETING)
-		vstatus.change_state(STATE_IDLE)
-		return
-	if not vstatus.time_to_idle:
-		vstatus.mk.set_message(GREETING)
-		vstatus.change_state(STATE_IDLE)
-		return
+	reset_idler(v, vstatus)
 
 def create_state_table(vstatus):
 	vstatus.state_table[(STATE_IDLE,TICK,1)] = handle_idle_tick
@@ -711,8 +678,7 @@ def run_forever(rfh, wfh, options, cf):
 	if USE_DB: db = DispenseDatabase(v, cf.DBServer, cf.DBName, cf.DBUser, cf.DBPassword)
 
 	setup_idlers(v)
-	choose_idler()
-	vstatus.mk.set_message(GREETING)
+	reset_idler(v, vstatus)
 
 	# This main loop was hideous and the work of the devil.
 	# This has now been fixed (mostly) - mtearle
@@ -725,8 +691,6 @@ def run_forever(rfh, wfh, options, cf):
 	#
 	# ( return state - not currently implemented )
 
-	vstatus.change_state(STATE_IDLE,1)
-
 	while True:
 		if USE_DB:
 			try:
@@ -735,7 +699,7 @@ def run_forever(rfh, wfh, options, cf):
 				logging.error('Database error: '+str(e))
 
 
-		e = v.next_event(0)
+		e = v.next_event(vstatus.time_of_next_idlestep-time())
 		(event, params) = e
 
 		run_handler(event, params, v, vstatus)
diff --git a/sql-edition/servers/VendingMachine.py b/sql-edition/servers/VendingMachine.py
index 7a06111..ee9b063 100644
--- a/sql-edition/servers/VendingMachine.py
+++ b/sql-edition/servers/VendingMachine.py
@@ -154,6 +154,7 @@ class VendingMachine:
 	def next_event(self, timeout = None):
 		# we don't want to buffer in the serial port, so we get all the events
 		# we can ASAP.
+		if timeout < 0: timeout = 0
 		if len(self.events) > 0: timeout = 0
 		while True:
 			(r, _, _) = select([self.rfh], [], [], timeout)
-- 
GitLab