Cleanups for elapsed detection logic.

vars/waitForAdapters.groovy
---------------------------
   o Extract waitOn= logic into a named function.
   o Added more data checks to consider an elapsed time as valid.
   o Replace list.find{} loop with for() to simplify earliest possible
     return when invalid elapsed values are detected.
   o Check adapter list size passed in to avoid GIGO.
   o Return a string describing state so logic is easier to follow.
   o Literal string state can also be displayed inline for debugging.
   o Wrap done loop with a explicit 10min (silly) timeout so no chance of going infinite.

Change-Id: I8a93d591e8f4dbf5267769792019789fb5a3e8e1
diff --git a/vars/waitForAdapters.groovy b/vars/waitForAdapters.groovy
index b8ba596..5d2f2a8 100644
--- a/vars/waitForAdapters.groovy
+++ b/vars/waitForAdapters.groovy
@@ -33,7 +33,7 @@
         // if that's the case we won't be able to check the timestamp so
         // it's useless to wait
         println("voltctl can't parse LastCommunication, skip waiting")
-	adapters = 'SKIP'
+	adapters = 'SKIP' // why not just retry a few times (?)
     }
 
     print("** ${iam}: returned $adapters")
@@ -41,6 +41,96 @@
 }
 
 // -----------------------------------------------------------------------
+// Intent: Interrogate adapter states to determine if we should continue
+// looping waiting for adapter to start.  Passed values must all be
+// Integral, Valid and within range to assert we are done waiting.
+// Function will return incomplete at the first sign of an invalid value.
+// -----------------------------------------------------------------------
+// NOTE: list.find{} was replaced with a for loop to simplify
+// early loop termination and return.
+// -----------------------------------------------------------------------
+// RETURN: scalar
+//   'DOUBLE-DIGIT' - value exceeds 0-5sec response window.
+//   'NON-NUMERIC'  - garbage in the stream (letter, symbol, ctrl)
+//   'NULL'         - empty or null string detected
+//   'VALID'        - succes, all adapters are functional
+//   'SIX-NINE'     - detected a single numeric digit between 6 and 9.
+// -----------------------------------------------------------------------
+def getAdaptersState(String adapters0)
+{
+    String iam = getIam('getAdaptersState')
+    Boolean debug = true // for now
+
+    def adapters = adapters0.split('\n')
+
+    String ans = null
+    def found = []
+    for(i=0; i<adapters.size(); i++)
+    {
+	String elapsed = args[i]
+	if (debug)
+	    println("** ${iam} Checking elapsed[$i]: $elapsed")
+
+	if (! elapsed) // empty string or null
+	{
+	    ans = 'NULL'
+	    break
+	}
+
+	Integer size = elapsed.length()
+	if (size > 2) // 463765h58m52(s)
+	{
+	    // higlander: there can be only one
+	    ans = 'DOUBLE-DIGIT'
+	    break
+	}
+
+	if (elapsed.endsWith('s'))
+	{
+	    // safer than replaceAll('s'): ssss1s => 1
+	    elapsed = elapsed.substring(0, size-1)
+	}
+
+	// Line noise guard: 'a', 'f', '#', '!'
+	if (! elapsed.isInteger())
+	{
+	    ans = 'NON-NUMERIC'
+	    break
+	}
+
+	// Is value in range:
+	//   discard negative integers as just plain wonky.
+	//   HMMM(?): zero/no latency response is kinda odd to include imho.
+	Integer val = elapsed.toInteger()
+	if (5 >= val && val >= 0)
+	{
+	    found.add(elapsed)
+	}
+	else
+	{
+	    ans = 'SIX-NINE'
+	    break
+	}
+    } // for()
+
+    // Declare success IFF all values are:
+    //    o integral
+    //    o valid
+    //    o within range
+    if (ans == null)
+    {
+	Integer got = found.size()
+	Integer exp = adapters.size()
+	ans = (! exp)      ? 'NO-ADAPTERS'
+            : (got == exp) ? 'VALID'
+            : 'CONTINUE'
+    }
+
+    println("** ${iam} return: [$ans]")
+    return ans
+} // getAdaptersState
+
+// -----------------------------------------------------------------------
 // -----------------------------------------------------------------------
 def process(Map config)
 {
@@ -83,28 +173,26 @@
     // if voltctl can't read LastCommunication we skip this check
 
     println("** ${iam}: Wait for adapter LastCommunication")
-    def done = false;
-    while (!done)
+    Integer countdown = 60 * 10
+    while (true)
     {
 	sleep 1
 	def adapters = getAdapters()
+	// Why are we not failing hard on this condition ?
+	// Adapters in an unknown state == testing: roll the dice
 	if (adapters == 'SKIP') { break }
 
-	def waitingOn = adapters.split( '\n' ).find{elapsed ->
-            elapsed = elapsed.replaceAll('s','') // remove seconds
-            print("** ${iam}: waitingOn elapsed=[${elapsed}]")
+	def state = getAdaptersState(adapters)
+	if (state == 'VALID') { break }   // IFF
 
-            // it has to be a single digit
-            if (elapsed.length() > 1) { // 463765h58m52s
-		return true
-            }
-
-	    Boolean is_valid = (5 >= (elapsed as Integer))
-	    print("** ${iam}: waitingOn: is_valid=[$is_valid]")
-	    return is_valid
-	}
-
-	done = (waitingOn == null || waitingOn.length() == 0)
+	// ----------------------------------------------------------
+	// Excessive timeout but unsure where startup time boundry is
+	// [TODO] profile for a baseline
+	// [TODO] groovy.transform.TimedInterrupt
+	// ----------------------------------------------------------
+	countdown -= 1
+	if (1 > countdown)
+	    throw new Exception("ERROR: Timed out waiting on adapter startup")
     }
 
     println("** ${iam}: Wait for adapter LastCommunication")