Adding an OLT state machine.

Initialized the OLT connection and maintaining a keepalive
Need to change implementation to use twisted

Change-Id: I670c2875b8c653a0849c85a2840089742dfa1188
diff --git a/voltha/adapters/microsemi/PAS5211.py b/voltha/adapters/microsemi/PAS5211.py
index 01a3b66..ec9e8b8 100644
--- a/voltha/adapters/microsemi/PAS5211.py
+++ b/voltha/adapters/microsemi/PAS5211.py
@@ -69,6 +69,7 @@
         LESignedIntField("onu_session_id", -1)
     ]
 
+
 class PAS5211MsgGetOltVersion(PAS5211Msg):
     opcode = 3
     name = "PAS5211MsgGetOltVersion"
@@ -77,6 +78,26 @@
     def answers(self, other):
         return other.name == "PAS5211MsgGetOltVersionResponse"
 
+
+class PAS5211MsgGetProtocolVersion(PAS5211Msg):
+    opcode = 2
+    name = "PAS5211MsgGetProtocolVersion"
+    fields_desc = [ ]
+
+
+class PAS5211MsgGetProtocolVersionResponse(PAS5211Msg):
+    name = "PAS5211MsgGetProtocolVersionResponse"
+    fields_desc = [
+        LEShortField("major_hardware_version", 0),
+        LEShortField("minor_hardware_version", 0),
+        LEShortField("major_pfi_version", 0),
+        LEShortField("minor_pfi_version", 0)
+    ]
+
+    def answers(self, other):
+        return other.name == 'PAS5211MsgGetProtocolVersion'
+
+
 class PAS5211MsgGetOltVersionResponse(PAS5211Msg):
     name = "PAS5211MsgGetOltVersionResponse"
     fields_desc = [
@@ -110,4 +131,5 @@
 split_layers(Dot3, LLC)
 bind_layers(Dot3,PAS5211FrameHeader)
 bind_layers(PAS5211FrameHeader, PAS5211MsgHeader)
-bind_layers(PAS5211MsgHeader, PAS5211MsgGetOltVersionResponse, opcode=0x3800 | 3)
\ No newline at end of file
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetOltVersionResponse, opcode=0x3800 | 3)
+bind_layers(PAS5211MsgHeader, PAS5211MsgGetProtocolVersionResponse, opcode=0x2800 | 2)
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/PAS5211_comm.py b/voltha/adapters/microsemi/PAS5211_comm.py
index a9fec63..2167a9d 100644
--- a/voltha/adapters/microsemi/PAS5211_comm.py
+++ b/voltha/adapters/microsemi/PAS5211_comm.py
@@ -63,10 +63,10 @@
         self.seqgen = sequence_generator(init)
         self.src_mac = determine_src_mac(self.iface)
 
-    def communicate(self, msg, **kwargs):
+    def communicate(self, msg, timeout=1, **kwargs):
         if self.src_mac is not None:
             frame = constructPAS5211Frames(msg, self.seqgen.next(), self.src_mac,
                                            self.dst_mac, **kwargs)
-            return srp1(frame, timeout=2, iface=self.iface)
+            return srp1(frame, timeout=timeout, iface=self.iface)
         else:
             log.info('Unknown src mac for {}'.format(self.iface))
diff --git a/voltha/adapters/microsemi/RubyAdapter.py b/voltha/adapters/microsemi/RubyAdapter.py
index 93c5b07..cdfec1a 100644
--- a/voltha/adapters/microsemi/RubyAdapter.py
+++ b/voltha/adapters/microsemi/RubyAdapter.py
@@ -19,10 +19,13 @@
 """
 import structlog
 from voltha.adapters.interface import IAdapterInterface
-from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetOltVersion
 from voltha.adapters.microsemi.PAS5211_comm import PAS5211Communication
-#from voltha.protos.adapter_pb2 import Adapter, AdapterConfig, DeviceTypes
-#from voltha.protos.health_pb2 import HealthStatus
+from voltha.adapters.microsemi.StateMachine import Disconnected
+import signal
+from voltha.protos.adapter_pb2 import Adapter, AdapterConfig, DeviceTypes
+from voltha.protos.health_pb2 import HealthStatus
+
+
 from zope.interface import implementer
 
 log = structlog.get_logger()
@@ -33,11 +36,15 @@
 class RubyAdapter(object):
     def __init__(self, config):
         self.config = config
-#        self.descriptor = Adapter(
-#            id='ruby',
-#            config=AdapterConfig()
-#            # TODO
-#        )
+        self.descriptor = Adapter(
+            id='ruby',
+            config=AdapterConfig()
+            # TODO
+        )
+        self.comm = comm = PAS5211Communication(dst_mac=olt_conf['olts']['mac'],
+                                                iface=olt_conf['iface'])
+        self.olt = Disconnected(comm)
+        signal.signal(signal.SIGINT, self.stop)
 
     def start(self):
         log.debug('starting')
@@ -46,6 +53,7 @@
 
     def stop(self):
         log.debug('stopping')
+        self.olt.disconnect()
         log.info('stopped')
 
     def adapter_descriptor(self):
@@ -53,13 +61,12 @@
 
     def device_types(self):
         pass
-#        return DeviceTypes(
-#            items=[]  # TODO
-#        )
+        return DeviceTypes(
+            items=[]  # TODO
+        )
 
     def health(self):
-        pass
-#        return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
+        return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
 
     def change_master_state(self, master):
         raise NotImplementedError()
@@ -74,10 +81,15 @@
         raise NotImplementedError()
 
     def init_olt(self):
-        comm = PAS5211Communication(dst_mac=olt_conf['olts']['mac'], iface=olt_conf['iface'])
-        packet = comm.communicate(PAS5211MsgGetOltVersion())
-        log.info('{}'.format(packet.show()))
-
+        self.olt.run()
+        self.olt = self.olt.transition()
+        self.olt.run()
+        self.olt = self.olt.transition()
+        self.olt.run()
 
 if __name__ == '__main__':
-    RubyAdapter(None).start()
\ No newline at end of file
+    try:
+        ruby = RubyAdapter(None)
+        ruby.start()
+    except KeyboardInterrupt:
+        ruby.stop()
\ No newline at end of file
diff --git a/voltha/adapters/microsemi/StateMachine.py b/voltha/adapters/microsemi/StateMachine.py
new file mode 100644
index 0000000..5ad1622
--- /dev/null
+++ b/voltha/adapters/microsemi/StateMachine.py
@@ -0,0 +1,155 @@
+#
+# Copyright 2016 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+Base OLT State machine class
+"""
+import threading
+import time
+from voltha.adapters.microsemi.PAS5211 import PAS5211MsgGetProtocolVersion, PAS5211MsgGetOltVersion
+
+class State(object):
+    def __init__(self):
+        pass
+
+    """
+    Attempt an operation towards the OLT
+    """
+    def run(self):
+        raise NotImplementedError()
+
+    """
+    Distates which state to transtion to.
+    Predicated on the run operation to be successful.
+    """
+    def transition(self):
+        raise NotImplementedError()
+
+    """
+    Returns any useful information for the given State
+    """
+    def value(self):
+        raise NotImplementedError()
+
+    """
+    Sends a message to this olt
+    """
+    def send_msg(self, msg):
+        raise NotImplementedError()
+
+    """
+    Disconnected an OLT.
+    """
+    def disconnect(self):
+        raise NotImplementedError()
+
+"""
+Represents an OLT in disconnected or pre init state.
+"""
+class Disconnected(State):
+
+    def __init__(self, pas_comm):
+        self.comm = pas_comm
+        self.completed = False
+        self.packet = None
+
+    def run(self):
+        self.packet = self.comm.communicate(PAS5211MsgGetProtocolVersion())
+        if self.packet is not None:
+            self.completed = True
+        return self.completed
+
+    def transition(self):
+        if self.completed:
+            return Fetch_Version(self.comm)
+
+    def value(self):
+        # TODO return a nicer value than the packet.
+        return self.packet
+
+    def send_msg(self, msg):
+        raise NotImplementedError()
+
+    def disconnect(self):
+        raise NotImplementedError()
+
+"""
+Fetches the OLT version
+"""
+class Fetch_Version(State):
+    def __init__(self, pas_comm):
+        self.comm = pas_comm
+        self.completed = False
+        self.packet = None
+
+    def run(self):
+        self.packet = self.comm.communicate(PAS5211MsgGetOltVersion())
+        if self.packet is not None:
+            self.completed = True
+        return self.completed
+
+    def transition(self):
+        if self.completed:
+            return Connected(self.comm)
+
+    def value(self):
+        # TODO return a nicer value than the packet.
+        return self.packet
+
+    def send_msg(self, msg):
+        raise NotImplementedError()
+
+    def disconnect(self):
+        raise NotImplementedError()
+
+
+"""
+OLT is in connected State
+"""
+class Connected(State):
+    def __init__(self, pas_comm):
+        self.comm = pas_comm
+        self.completed = False
+        self.packet = None
+        self.t = threading.Thread(target = self.keepalive)
+
+    def run(self):
+        self.t.start()
+
+    def transition(self):
+        if self.completed:
+            return Disconnected(self.comm)
+
+    def value(self):
+        # TODO return a nicer value than the packet.
+        return self.packet
+
+    def send_msg(self, msg):
+        return self.comm.communicate(msg)
+
+    # FIXME replace with twisted
+    def keepalive(self):
+        while not self.completed:
+            self.packet = self.comm.communicate(PAS5211MsgGetOltVersion())
+            if self.packet is not None:
+                time.sleep(1)
+            else:
+                break
+        self.completed = True
+
+    def disconnect(self):
+        print "Stopping"
+        self.completed = True