VOL-513: Support ASFVOLT16 Adapter in Swarm Mode -VOL-467 Phase 1 - Polling Mode

Change-Id: I0cdc955044e8a8160857a0bedcc234c5d18f7d11
(cherry picked from commit 8882642a1cb6b6c02a1bc3d1aee2787b7ee6718f)
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index c4279fe..b589398 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -336,6 +336,7 @@
                                   port_type=Port.ETHERNET_NNI,
                                   device_id=device.id,
                                   logical_device_id=self.logical_device_id)
+            reactor.callInThread(self.bal.get_indication_info, self.device_id)
 
         self.bal.activate_olt()
 
@@ -578,7 +579,6 @@
 
     def BalIfaceIndication(self, device_id, Iface_ID):
         self.log.info('Interface Indication')
-        import pdb;pdb.set_trace()
         device = self.adapter_agent.get_device(self.device_id)
         self._handle_pon_pm_counter_req_towards_device(device,Iface_ID)
 
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py
new file mode 100755
index 0000000..6648376
--- /dev/null
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py
@@ -0,0 +1,387 @@
+#
+# Copyright 2017 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.
+#
+
+"""
+Asfvolt16 OLT adapter
+"""
+from twisted.internet import reactor
+from common.utils.grpc_utils import twisted_async
+from voltha.adapters.asfvolt16_olt.protos import bal_indications_pb2
+from voltha.adapters.asfvolt16_olt.protos import bal_model_types_pb2, \
+    bal_errno_pb2, bal_pb2, bal_model_ids_pb2
+from voltha.adapters.asfvolt16_olt.grpc_server import GrpcServer
+
+
+class Asfvolt16IndHandler(object):
+    def __init__(self, log):
+        self.log = log
+
+    def bal_acc_term_oper_sta_cng_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'access_terminal_indication'
+        ind_info['_sub_group_type'] = 'oper_state_change'
+        bal_err = bal_pb2.BalErr()
+        return bal_err
+
+    def bal_acc_term_ind(self, indication, device_handler):
+        #     ind_info: {'_object_type': <str>
+        #                'actv_status': <str>}
+        ind_info = dict()
+        ind_info['_object_type'] = 'access_terminal_indication'
+        ind_info['_sub_group_type'] = 'access_terminal_indication'
+        if indication.access_term_ind.data.admin_state == \
+                bal_model_types_pb2.BAL_STATE_UP:
+            ind_info['activation_successful'] = True
+        else:
+            ind_info['activation_successful'] = False
+
+        reactor.callLater(0,
+                          device_handler.handle_access_term_ind,
+                          ind_info,
+                          indication.access_term_ind.key.access_term_id)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_flow_oper_sts_cng(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'flow_indication'
+        ind_info['_sub_group_type'] = 'oper_state_change'
+        ind_info['_object_type'] = indication.objType
+        ind_info['_sub_group_type'] = indication.sub_group
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_flow_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'flow_indication'
+        ind_info['_sub_group_type'] = 'flow_indication'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_group_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'group_indication'
+        ind_info['_sub_group_type'] = 'group_indication'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_iface_oper_sts_cng(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'interface_indication'
+        ind_info['_sub_group_type'] = 'oper_state_change'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_iface_los(self, indication, device_handler):
+        los_status = indication.interface_los.data.status
+        if los_status != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            balIfaceLos_dict = {}
+            balIfaceLos_dict["los_status"] = los_status.__str__()
+            reactor.callLater(0, \
+                              device_handler.BalIfaceLosAlarm, \
+                              indication.device_id, \
+                              indication.interface_los.key.intf_id, \
+                              los_status, balIfaceLos_dict)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_iface_ind(self, indication, device_handler):
+        self.log.info('Awaiting ONU discovery')
+        reactor.callLater(0,\
+                          device_handler.BalIfaceIndication,\
+                          indication.device_id.decode('unicode-escape'),\
+                          indication.interface_ind.key.intf_id)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_iface_stat(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'interface_indication'
+        ind_info['_sub_group_type'] = 'stat_indication'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_subs_term_oper_sts_cng(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'sub_term_indication'
+        ind_info['_sub_group_type'] = 'oper_state_change'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_subs_term_discovery_ind(self, indication, device_handler):
+        #     ind_info: {'object_type': <int>
+        #                '_sub_group_type': <str>
+        #                '_device_id': <str>
+        #                '_pon_id' : <int>
+        #                'onu_id' : <int>
+        #                '_vendor_id' : <str>
+        #                '__vendor_specific' : <str>
+        #                'activation_successful':[True or False]}
+        onu_data = indication.terminal_disc
+        ind_info = dict()
+        ind_info['_object_type'] = 'sub_term_indication'
+        ind_info['_sub_group_type'] = 'onu_discovery'
+        ind_info['_pon_id'] = onu_data.key.intf_id
+        ind_info['onu_id'] = onu_data.key.sub_term_id
+        ind_info['_vendor_id'] = onu_data.data.serial_number.vendor_id
+        ind_info['_vendor_specific'] = \
+            onu_data.data.serial_number.vendor_specific
+        reactor.callLater(0,
+                          device_handler.handle_sub_term_ind,
+                          ind_info)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_subs_term_alarm_ind(self, indication, device_handler):
+        # Loss of signal
+        los = indication.terminal_alarm.data.alarm.los
+        # Loss of busrt
+        lob = indication.terminal_alarm.data.alarm.lob
+        # Loss of PLOAM miss channel
+        lopc_miss = indication.terminal_alarm.data.alarm.lopc_miss
+        # Loss of PLOAM channel
+        lopc_mic_error = indication.terminal_alarm.data.alarm.lopc_mic_error
+
+        balSubTermAlarm_Dict = {}
+        balSubTermAlarm_Dict["LOS Status"] = los.__str__()
+        balSubTermAlarm_Dict["LOB Status"] = lob.__str__()
+        balSubTermAlarm_Dict["LOPC MISS Status"] = lopc_miss.__str__()
+        balSubTermAlarm_Dict["LOPC MIC ERROR Status"] = lopc_mic_error.__str__()
+
+        if los != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            reactor.callLater(0, device_handler.BalSubsTermLosAlarm, \
+                              indication.terminal_alarm.key.intf_id, \
+                              los, balSubTermAlarm_Dict)
+
+        if lob != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            reactor.callLater(0, device_handler.BalSubsTermLobAlarm, \
+                              indication.terminal_alarm.key.intf_id, \
+                              lob, balSubTermAlarm_Dict)
+
+        if lopc_miss != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            reactor.callLater(0, device_handler.BalSubsTermLopcMissAlarm, \
+                              indication.terminal_alarm.key.intf_id, \
+                              lopc_miss, balSubTermAlarm_Dict)
+
+        if lopc_mic_error != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            reactor.callLater(0, device_handler.BalSubsTermLopcMicErrorAlarm, \
+                              indication.terminal_alarm.key.intf_id, \
+                              lopc_mic_error, balSubTermAlarm_Dict)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_subs_term_dgi_ind(self, indication, device_handler):
+        #     ind_info: {'_object_type': <str>
+        #                '_device_id': <str>
+        #                '_pon_id' : <int>
+        #                'onu_id' : <int>
+        #                '_vendor_id' : <str>
+        #                '__vendor_specific' : <str>
+        #                'activation_successful':[True or False]}
+        dgi_status = indication.terminal_dgi.data.dgi_status
+        if dgi_status != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
+            ind_info = dict()
+            ind_info['_object_type'] = 'sub_term_indication'
+            ind_info['_sub_group_type'] = 'dgi_indication'
+
+            balSubTermDgi_Dict = {}
+            balSubTermDgi_Dict["dgi_status"] = dgi_status.__str__()
+            reactor.callLater(0,
+                              device_handler.BalSubsTermDgiAlarm, \
+                              indication.device_id, \
+                              indication.terminal_dgi.key.intf_id,\
+                              indication.terminal_dgi.key.sub_term_id, \
+                              dgi_status,balSubTermDgi_Dict, ind_info)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_subs_term_ind(self, indication, device_handler):
+        #     ind_info: {'_object_type': <str>
+        #                '_sub_group_type': <str>
+        #                '_device_id': <str>
+        #                '_pon_id' : <int>
+        #                'onu_id' : <int>
+        #                '_vendor_id' : <str>
+        #                '__vendor_specific' : <str>
+        #                'activation_successful':[True or False]}
+        onu_data = indication.terminal_ind
+        ind_info = dict()
+        ind_info['_object_type'] = 'sub_term_indication'
+        ind_info['_sub_group_type'] = 'sub_term_indication'
+        ind_info['_pon_id'] = onu_data.key.intf_id
+        ind_info['onu_id'] = onu_data.key.sub_term_id
+        ind_info['_vendor_id'] = onu_data.data.serial_number.vendor_id
+        ind_info['_vendor_specific'] = \
+            onu_data.data.serial_number.vendor_specific
+        if (bal_model_types_pb2.BAL_STATE_DOWN == onu_data.data.admin_state):
+            ind_info['activation_successful'] = False
+        elif (bal_model_types_pb2.BAL_STATE_UP == onu_data.data.admin_state):
+            ind_info['activation_successful'] = True
+
+        reactor.callLater(0,
+                          device_handler.handle_sub_term_ind,
+                          ind_info)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_tm_queue_ind_info(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'tm_q_indication'
+        ind_info['_sub_group_type'] = 'tm_q_indication'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_tm_sched_ind_info(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'tm_sched_indication'
+        ind_info['_sub_group_type'] = 'tm_sched_indication'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_pkt_bearer_channel_rx_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['flow_id'] = indication.pktData.data.flow_id
+        ind_info['flow_type'] = indication.pktData.data.flow_type
+        ind_info['intf_id'] = indication.pktData.data.intf_id
+        ind_info['intf_type'] = indication.pktData.data.intf_type
+        ind_info['svc_port'] = indication.pktData.data.svc_port
+        ind_info['flow_cookie'] = indication.pktData.data.flow_cookie
+        ind_info['packet'] = indication.pktData.data.pkt
+        reactor.callLater(0,
+                          device_handler.handle_packet_in,
+                          ind_info)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_pkt_omci_channel_rx_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'packet_in_indication'
+        ind_info['_sub_group_type'] = 'omci_message'
+        packet_data = indication.balOmciResp.key.packet_send_dest
+        ind_info['onu_id'] = packet_data.itu_omci_channel.sub_term_id
+        ind_info['packet'] = indication.balOmciResp.data.pkt
+        self.log.info('ONU Id is',
+                      onu_id=packet_data.itu_omci_channel.sub_term_id)
+        reactor.callLater(0,
+                          device_handler.handle_omci_ind,
+                          ind_info)
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def bal_pkt_ieee_oam_channel_rx_ind(self, indication, device_handler):
+        ind_info = dict()
+        ind_info['_object_type'] = 'packet_in_indication'
+        ind_info['_sub_group_type'] = 'ieee_oam_message'
+        bal_err = bal_pb2.BalErr()
+        bal_err.err = bal_errno_pb2.BAL_ERR_OK
+        return bal_err
+
+    def handle_indication_from_bal(self, bal_ind, device_handler):
+        indication_handler = self.indication_handler_map.get((bal_ind.objType,
+                                                              bal_ind.sub_group),
+                                                              None)
+        if indication_handler is None:
+            self.log.debug('No handler', objType=bal_ind.objType,
+                                         sub_group=bal_ind.sub_group)
+            pass  # no-op
+        else:
+            indication_handler(self, bal_ind, device_handler)
+
+    indication_handler_map = {
+        (bal_model_ids_pb2.BAL_OBJ_ID_ACCESS_TERMINAL,
+         bal_model_ids_pb2.BAL_ACCESS_TERMINAL_AUTO_ID_IND):
+            bal_acc_term_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_ACCESS_TERMINAL,
+         bal_model_ids_pb2.BAL_ACCESS_TERMINAL_AUTO_ID_OPER_STATUS_CHANGE):
+            bal_acc_term_oper_sta_cng_ind,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_FLOW,
+         bal_model_ids_pb2.BAL_FLOW_AUTO_ID_OPER_STATUS_CHANGE):
+            bal_flow_oper_sts_cng,
+        (bal_model_ids_pb2.BAL_OBJ_ID_FLOW,
+         bal_model_ids_pb2.BAL_FLOW_AUTO_ID_IND):
+            bal_flow_ind,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_GROUP,
+         bal_model_ids_pb2.BAL_GROUP_AUTO_ID_IND):
+            bal_group_ind,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_INTERFACE,
+         bal_model_ids_pb2.BAL_INTERFACE_AUTO_ID_IND):
+            bal_iface_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_INTERFACE,
+         bal_model_ids_pb2.BAL_INTERFACE_AUTO_ID_LOS):
+            bal_iface_los,
+        (bal_model_ids_pb2.BAL_OBJ_ID_INTERFACE,
+         bal_model_ids_pb2.BAL_INTERFACE_AUTO_ID_IND):
+            bal_iface_oper_sts_cng,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL,
+         bal_model_ids_pb2.\
+         BAL_SUBSCRIBER_TERMINAL_AUTO_ID_OPER_STATUS_CHANGE):
+            bal_subs_term_oper_sts_cng,
+        (bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL,
+         bal_model_ids_pb2.\
+         BAL_SUBSCRIBER_TERMINAL_AUTO_ID_SUB_TERM_DISC):
+            bal_subs_term_discovery_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL,
+         bal_model_ids_pb2.\
+         BAL_SUBSCRIBER_TERMINAL_AUTO_ID_SUB_TERM_ALARM):
+            bal_subs_term_alarm_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL,
+         bal_model_ids_pb2.\
+         BAL_SUBSCRIBER_TERMINAL_AUTO_ID_DGI):
+            bal_subs_term_dgi_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL,
+         bal_model_ids_pb2.\
+         BAL_SUBSCRIBER_TERMINAL_AUTO_ID_IND):
+            bal_subs_term_ind,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_TM_QUEUE,
+         bal_model_ids_pb2.BAL_TM_QUEUE_AUTO_ID_IND):
+            bal_tm_queue_ind_info,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_TM_SCHED,
+         bal_model_ids_pb2.BAL_TM_SCHED_AUTO_ID_IND):
+            bal_tm_sched_ind_info,
+
+        (bal_model_ids_pb2.BAL_OBJ_ID_PACKET,
+         bal_model_ids_pb2.BAL_PACKET_AUTO_ID_BEARER_CHANNEL_RX):
+            bal_pkt_bearer_channel_rx_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_PACKET,
+         bal_model_ids_pb2.BAL_PACKET_AUTO_ID_ITU_OMCI_CHANNEL_RX):
+            bal_pkt_omci_channel_rx_ind,
+        (bal_model_ids_pb2.BAL_OBJ_ID_PACKET,
+         bal_model_ids_pb2.BAL_PACKET_AUTO_ID_IEEE_OAM_CHANNEL_RX):
+            bal_pkt_ieee_oam_channel_rx_ind,
+    }
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
index 64b3ca6..7113aae 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_rx_handler.py
@@ -23,6 +23,7 @@
 from voltha.adapters.asfvolt16_olt.protos import bal_model_types_pb2, \
     bal_errno_pb2, bal_pb2
 from voltha.adapters.asfvolt16_olt.grpc_server import GrpcServer
+from voltha.adapters.asfvolt16_olt.asfvolt16_ind_handler import Asfvolt16IndHandler
 
 
 class Asfvolt16RxHandler(object):
@@ -34,6 +35,7 @@
         self.grpc_server = None
         self.grpc_server_port = port
         self.log = log
+        self.ind_handler = Asfvolt16IndHandler(log)
 
     def start(self):
         self.grpc_server = GrpcServer(self.grpc_server_port, self, self.log)
@@ -48,9 +50,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'access_terminal_indication'
-        ind_info['_sub_group_type'] = 'oper_state_change'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_acc_term_oper_sts_cng_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -60,21 +61,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Received access terminal Indication',
                       device_id=device_id, obj_type=request.objType)
-        #     ind_info: {'_object_type': <str>
-        #                'actv_status': <str>}
-        ind_info = dict()
-        ind_info['_object_type'] = 'access_terminal_indication'
-        ind_info['_sub_group_type'] = 'access_terminal_indication'
-        if request.access_term_ind.data.admin_state == \
-                bal_model_types_pb2.BAL_STATE_UP:
-            ind_info['activation_successful'] = True
-        else:
-            ind_info['activation_successful'] = False
-
         device_handler = self.adapter.devices_handlers[device_id]
-        reactor.callLater(0,
-                          device_handler.handle_access_term_ind,
-                          ind_info,request.access_term_ind.key.access_term_id)
+        self.ind_handler.bal_acc_term_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -84,11 +72,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'flow_indication'
-        ind_info['_sub_group_type'] = 'oper_state_change'
-        ind_info['_object_type'] = request.objType
-        ind_info['_sub_group_type'] = request.sub_group
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_flow_oper_sts_cng(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -98,9 +83,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'flow_indication'
-        ind_info['_sub_group_type'] = 'flow_indication'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_flow_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -110,9 +94,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'group_indication'
-        ind_info['_sub_group_type'] = 'group_indication'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_group_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -122,9 +105,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'interface_indication'
-        ind_info['_sub_group_type'] = 'oper_state_change'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_iface_oper_sts_cng(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -134,21 +116,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Interface Loss Of Signal Alarm',\
                       device_id=device_id, obj_type=request.objType)
-        los_status = request.interface_los.data.status
-        if los_status != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-
-           balIfaceLos_dict = { }
-           balIfaceLos_dict["los_status"]=los_status.__str__()
-
-           device_handler = self.adapter.devices_handlers[device_id]
-           reactor.callLater(0,\
-                             device_handler.BalIfaceLosAlarm,\
-                             device_id,request.interface_los.key.intf_id,\
-                             los_status,balIfaceLos_dict)
-
-        ind_info = dict()
-        ind_info['_object_type'] = 'interface_indication'
-        ind_info['_sub_group_type'] = 'loss_of_signal'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_iface_los(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -158,10 +127,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Interface indication Received',
                       device_id=device_id, obj_type=request.objType)
-        self.log.info('Awaiting ONU discovery')
-        reactor.callLater(0,\
-                          device_handler.BalIfaceIndication,\
-                          device_id,request.interface_ind.key.intf_id)
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_iface_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -171,9 +138,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'interface_indication'
-        ind_info['_sub_group_type'] = 'stat_indication'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_iface_stat(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -183,41 +149,19 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'sub_term_indication'
-        ind_info['_sub_group_type'] = 'oper_state_change'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_subs_term_oper_sts_cng(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
 
     @twisted_async
     def BalSubsTermDiscoveryInd(self, request, context):
-        #     ind_info: {'object_type': <int>
-        #                '_sub_group_type': <str>
-        #                '_device_id': <str>
-        #                '_pon_id' : <int>
-        #                'onu_id' : <int>
-        #                '_vendor_id' : <str>
-        #                '__vendor_specific' : <str>
-        #                'activation_successful':[True or False]}
-
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Subscriber terminal discovery Indication',
                       device_id=device_id, obj_type=request.objType)
-        onu_data = request.terminal_disc
-        ind_info = dict()
-        ind_info['_object_type'] = 'sub_term_indication'
-        ind_info['_sub_group_type'] = 'onu_discovery'
-        ind_info['_device_id'] = device_id
-        ind_info['_pon_id'] = onu_data.key.intf_id
-        ind_info['onu_id'] = onu_data.key.sub_term_id
-        ind_info['_vendor_id'] = onu_data.data.serial_number.vendor_id
-        ind_info['_vendor_specific'] = \
-            onu_data.data.serial_number.vendor_specific
         device_handler = self.adapter.devices_handlers[device_id]
-        reactor.callLater(0,
-                          device_handler.handle_sub_term_ind,
-                          ind_info)
+        self.ind_handler.bal_subs_term_discovery_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -227,115 +171,30 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('ONU Alarms for Subscriber Terminal',\
                       device_id=device_id, obj_type=request.objType)
-        #Loss of signal
-        los = request.terminal_alarm.data.alarm.los
-        #Loss of busrt
-        lob = request.terminal_alarm.data.alarm.lob
-        #Loss of PLOAM miss channel
-        lopc_miss = request.terminal_alarm.data.alarm.lopc_miss
-        #Loss of PLOAM channel
-        lopc_mic_error = request.terminal_alarm.data.alarm.lopc_mic_error
-
-        balSubTermAlarm_Dict = { }
-        balSubTermAlarm_Dict["LOS Status"]=los.__str__()
-        balSubTermAlarm_Dict["LOB Status"]=lob.__str__()
-        balSubTermAlarm_Dict["LOPC MISS Status"]=lopc_miss.__str__()
-        balSubTermAlarm_Dict["LOPC MIC ERROR Status"]=lopc_mic_error.__str__()
-
         device_handler = self.adapter.devices_handlers[device_id]
-
-        if los != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-           reactor.callLater(0, device_handler.BalSubsTermLosAlarm,\
-                             device_id,request.terminal_alarm.key.intf_id,\
-                             los, balSubTermAlarm_Dict)
-
-        if lob != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-           reactor.callLater(0, device_handler.BalSubsTermLobAlarm,\
-                             device_id,request.terminal_alarm.key.intf_id,\
-                             lob, balSubTermAlarm_Dict)
-
-        if lopc_miss != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-           reactor.callLater(0, device_handler.BalSubsTermLopcMissAlarm,\
-                             device_id,request.terminal_alarm.key.intf_id,\
-                             lopc_miss, balSubTermAlarm_Dict)
-
-        if lopc_mic_error != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-           reactor.callLater(0, device_handler.BalSubsTermLopcMicErrorAlarm,\
-                             device_id,request.terminal_alarm.key.intf_id,\
-                             lopc_mic_error, balSubTermAlarm_Dict)
-
+        self.ind_handler.bal_subs_term_alarm_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
 
     @twisted_async
     def BalSubsTermDgiInd(self, request, context):
-        self.log.info('Subscriber terminal Indication received')
-        #     ind_info: {'_object_type': <str>
-        #                '_device_id': <str>
-        #                '_pon_id' : <int>
-        #                'onu_id' : <int>
-        #                '_vendor_id' : <str>
-        #                '__vendor_specific' : <str>
-        #                'activation_successful':[True or False]}
         device_id = request.device_id.decode('unicode-escape')
-        self.log.info('Subscriber terminal dying gasp',\
+        self.log.info('Subscriber terminal dying gasp', \
                       device_id=device_id, obj_type=request.objType)
-
-        dgi_status = request.terminal_dgi.data.dgi_status
-
-        if dgi_status != bal_model_types_pb2.BAL_ALARM_STATUS_NO__CHANGE:
-
-           ind_info = dict()
-           ind_info['_object_type'] = 'sub_term_indication'
-           ind_info['_sub_group_type'] = 'dgi_indication'
-
-           balSubTermDgi_Dict = { }
-           balSubTermDgi_Dict["dgi_status"]=dgi_status.__str__()
-
-           device_handler = self.adapter.devices_handlers[device_id]
-           reactor.callLater(0,
-                             device_handler.BalSubsTermDgiAlarm,
-                             device_id,request.terminal_dgi.key.intf_id,\
-                             request.terminal_dgi.key.sub_term_id, \
-                             dgi_status,balSubTermDgi_Dict, ind_info)
-
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_subs_term_dgi_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
 
     @twisted_async
     def BalSubsTermInd(self, request, context):
-        #     ind_info: {'_object_type': <str>
-        #                '_sub_group_type': <str>
-        #                '_device_id': <str>
-        #                '_pon_id' : <int>
-        #                'onu_id' : <int>
-        #                '_vendor_id' : <str>
-        #                '__vendor_specific' : <str>
-        #                'activation_successful':[True or False]}
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Subscriber terminal indication received',
                       device_id=device_id, obj_type=request.objType)
-        onu_data = request.terminal_ind
-        ind_info = dict()
-        ind_info['_object_type'] = 'sub_term_indication'
-        ind_info['_sub_group_type'] = 'sub_term_indication'
-        ind_info['_device_id'] = device_id
-        ind_info['_pon_id'] = onu_data.key.intf_id
-        ind_info['onu_id'] = onu_data.key.sub_term_id
-        ind_info['_vendor_id'] = onu_data.data.serial_number.vendor_id
-        ind_info['_vendor_specific'] = \
-            onu_data.data.serial_number.vendor_specific
-        if (bal_model_types_pb2.BAL_STATE_DOWN == onu_data.data.admin_state):
-            ind_info['activation_successful'] = False
-        elif (bal_model_types_pb2.BAL_STATE_UP == onu_data.data.admin_state):
-            ind_info['activation_successful'] = True
-
         device_handler = self.adapter.devices_handlers[device_id]
-        reactor.callLater(0,
-                          device_handler.handle_sub_term_ind,
-                          ind_info)
+        self.ind_handler.bal_subs_term_ind(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -345,9 +204,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'tm_q_indication'
-        ind_info['_sub_group_type'] = 'tm_q_indication'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_tm_queue_ind_info(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -357,9 +215,8 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'tm_sched_indication'
-        ind_info['_sub_group_type'] = 'tm_sched_indication'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_tm_sched_ind_info(request, device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -369,18 +226,9 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Received Packet-In',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['flow_id'] = request.pktData.data.flow_id
-        ind_info['flow_type'] = request.pktData.data.flow_type
-        ind_info['intf_id'] = request.pktData.data.intf_id
-        ind_info['intf_type'] = request.pktData.data.intf_type
-        ind_info['svc_port'] = request.pktData.data.svc_port
-        ind_info['flow_cookie'] = request.pktData.data.flow_cookie
-        ind_info['packet'] = request.pktData.data.pkt
         device_handler = self.adapter.devices_handlers[device_id]
-        reactor.callLater(0,
-                          device_handler.handle_packet_in,
-                          ind_info)
+        self.ind_handler.bal_pkt_bearer_channel_rx_ind(request,
+                                                       device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -390,20 +238,9 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Received OMCI Messages',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'packet_in_indication'
-        ind_info['_sub_group_type'] = 'omci_message'
-        ind_info['_device_id'] = device_id
-        packet_data = request.balOmciResp.key.packet_send_dest
-        ind_info['onu_id'] = packet_data.itu_omci_channel.sub_term_id
-        ind_info['packet'] = request.balOmciResp.data.pkt
-        self.log.info('ONU Id is',
-                      onu_id=packet_data.itu_omci_channel.sub_term_id)
-
         device_handler = self.adapter.devices_handlers[device_id]
-        reactor.callLater(0,
-                          device_handler.handle_omci_ind,
-                          ind_info)
+        self.ind_handler.bal_pkt_omci_channel_rx_ind(request,
+                                                     device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
@@ -413,9 +250,9 @@
         device_id = request.device_id.decode('unicode-escape')
         self.log.info('Not implemented yet',
                       device_id=device_id, obj_type=request.objType)
-        ind_info = dict()
-        ind_info['_object_type'] = 'packet_in_indication'
-        ind_info['_sub_group_type'] = 'ieee_oam_message'
+        device_handler = self.adapter.devices_handlers[device_id]
+        self.ind_handler.bal_pkt_ieee_oam_channel_rx_ind(request,
+                                                         device_handler)
         bal_err = bal_pb2.BalErr()
         bal_err.err = bal_errno_pb2.BAL_ERR_OK
         return bal_err
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 6416804..5f68ac5 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -16,9 +16,13 @@
 
 from twisted.internet.defer import inlineCallbacks, returnValue
 from voltha.adapters.asfvolt16_olt.protos import bal_pb2, \
-    bal_model_types_pb2, bal_model_ids_pb2
+    bal_model_types_pb2, bal_model_ids_pb2, bal_indications_pb2
 from voltha.adapters.asfvolt16_olt.grpc_client import GrpcClient
+from voltha.adapters.asfvolt16_olt.asfvolt16_ind_handler \
+                                       import Asfvolt16IndHandler
 from common.utils.nethelpers import get_my_primary_local_ipv4
+from common.utils.asleep import asleep
+import time
 import os
 
 """
@@ -32,14 +36,23 @@
         self.log = log
         self.grpc_client = GrpcClient(self.log)
         self.stub = None
+        self.ind_stub = None
         self.device_id = None
         self.olt = olt
+        self.interval = 0.1
+        self.ind_obj = Asfvolt16IndHandler(log)
 
     @inlineCallbacks
     def connect_olt(self, host_and_port, device_id):
         self.device_id = device_id
         self.grpc_client.connect(host_and_port)
         self.stub = bal_pb2.BalStub(self.grpc_client.channel)
+        self.ind_stub = bal_indications_pb2.BalGetIndStub(self.grpc_client.channel)
+        self.olt.running = True
+
+        # Right now Bi-Directional GRPC support is not there in grpc-c.
+        # This code may be needed when bidirectional supported added
+        # in GRPC-C
         init = bal_pb2.BalInit()
         try:
             os.environ["SERVICE_HOST_IP"]
@@ -50,16 +63,10 @@
 
         ip_port = []
         ip_port.append(str(adapter_ip))
-        #ip_port.append("192.168.140.34")
         ip_port.append(":")
         ip_port.append(str(ADAPTER_PORT))
         init.voltha_adapter_ip_port ="".join(ip_port)
         self.log.info('Adapter port Ip', init.voltha_adapter_ip_port)
-
-        '''
-        TODO: Need to determine out what information
-        needs to be sent to the OLT at this stage.
-        '''
         self.log.info('connecting-olt', host_and_port=host_and_port,
                       init_details=init)
         yield self.stub.BalApiInit(init)
@@ -155,22 +162,30 @@
         obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_PACKET
         if pkt_info['dest_type'] == 'onu':
             # Set the destination ONU info
-            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_SUB_TERM
-            obj.packet.key.packet_send_dest.sub_term.sub_term_id = pkt_info['onu_id']
+            obj.packet.key.packet_send_dest.type = \
+                bal_model_types_pb2.BAL_DEST_TYPE_SUB_TERM
+            obj.packet.key.packet_send_dest.sub_term.sub_term_id = \
+                pkt_info['onu_id']
             # TODO: Need to provide correct values for sub_term_uni and int_id
-            #obj.packet.key.packet_send_dest.sub_term.sub_term_uni = egress_port
+            # obj.packet.key.packet_send_dest.sub_term.sub_term_uni = egress_port
             obj.packet.key.packet_send_dest.sub_term.intf_id = pkt_info['intf_id']
             obj.packet.data.intf_type = bal_model_types_pb2.BAL_INTF_TYPE_PON
         elif pkt_info['dest_type'] == 'gem_port':
-            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_SVC_PORT
-            obj.packet.key.packet_send_dest.svc_port.svc_port_id = pkt_info['gem_port']
-            obj.packet.key.packet_send_dest.svc_port.intf_id = pkt_info['intf_id']
+            obj.packet.key.packet_send_dest.type = \
+                bal_model_types_pb2.BAL_DEST_TYPE_SVC_PORT
+            obj.packet.key.packet_send_dest.svc_port.svc_port_id = \
+                pkt_info['gem_port']
+            obj.packet.key.packet_send_dest.svc_port.intf_id = \
+                pkt_info['intf_id']
             obj.packet.data.intf_type = bal_model_types_pb2.BAL_INTF_TYPE_PON
         elif pkt_info['dest_type'] == 'nni':
-            obj.packet.key.packet_send_dest.type = bal_model_types_pb2.BAL_DEST_TYPE_NNI
-            obj.packet.key.packet_send_dest.nni.intf_id = pkt_info['intf_id']
+            obj.packet.key.packet_send_dest.type = \
+                bal_model_types_pb2.BAL_DEST_TYPE_NNI
+            obj.packet.key.packet_send_dest.nni.intf_id = \
+                pkt_info['intf_id']
         else:
-            self.log.error('unsupported-dest-type', dest_type=pkt_info['dest_type'])
+            self.log.error('unsupported-dest-type',
+                           dest_type=pkt_info['dest_type'])
 
         # Set the Packet-out info
         # TODO: Need to provide correct value for intf_id
@@ -201,7 +216,7 @@
 
             obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_UP
             obj.flow.data.access_int_id = intf_id
-            #obj.flow.data.network_int_id = intf_id
+            # obj.flow.data.network_int_id = intf_id
             obj.flow.data.sub_term_id = onu_id
             obj.flow.data.svc_port_id = gem_port
             obj.flow.data.classifier.presence_mask = 0
@@ -320,7 +335,7 @@
 
             obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
             obj.flow.data.access_int_id = intf_id
-            #obj.flow.data.network_int_id = intf_id
+            # obj.flow.data.network_int_id = intf_id
             obj.flow.data.sub_term_id = onu_id
             self.log.info('deleting-flows-from-OLT-Device',
                           flow_details=obj)
@@ -330,7 +345,6 @@
                           flow_id, onu_id, exc=str(e))
         return
 
-
     @inlineCallbacks
     def create_scheduler(self, id, direction, owner_info, num_priority):
         try:
@@ -340,7 +354,7 @@
             obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_TM_SCHED
             # Fill Access Terminal Details
             if direction == 'downstream':
-                obj.tm_sched_cfg.key.dir =\
+                obj.tm_sched_cfg.key.dir = \
                     bal_model_types_pb2.BAL_TM_SCHED_DIR_DS
             else:
                 obj.tm_sched_cfg.key.dir = \
@@ -351,7 +365,7 @@
                 obj.tm_sched_cfg.data.owner.type = \
                     bal_model_types_pb2.BAL_TM_SCHED_OWNER_TYPE_AGG_PORT
                 obj.tm_sched_cfg.data.owner.agg_port.presence_mask = 0
-                obj.tm_sched_cfg.data.owner.agg_port.intf_id =\
+                obj.tm_sched_cfg.data.owner.agg_port.intf_id = \
                     owner_info['intf_id']
                 obj.tm_sched_cfg.data.owner.agg_port.presence_mask |= \
                     bal_model_types_pb2.BAL_TM_SCHED_OWNER_AGG_PORT_ID_INTF_ID
@@ -390,7 +404,8 @@
             obj.intf_id = intf_id
             obj.intf_type = interface_type
             stats = yield self.stub.BalCfgStatGet(obj)
-            self.log.info('Fetching statistics success', stats_data = stats.data)
+            self.log.info('Fetching statistics success',
+                          stats_data=stats.data)
             returnValue(stats)
         except Exception as e:
             self.log.info('Fetching statistics failed', exc=str(e))
@@ -399,10 +414,10 @@
     def set_bal_reboot(self, device_id):
         self.log.info('Set Reboot')
         try:
-            obj =  bal_pb2.BalReboot()
+            obj = bal_pb2.BalReboot()
             obj.device_id = device_id
             err = yield self.stub.BalApiReboot(obj)
-            self.log.info('OLT Reboot Success', reboot_err= err)
+            self.log.info('OLT Reboot Success', reboot_err=err)
             returnValue(err)
         except Exception as e:
             self.log.info('OLT Reboot failed', exc=str(e))
@@ -411,10 +426,26 @@
     def get_bal_heartbeat(self, device_id):
         self.log.info('Get HeartBeat')
         try:
-            obj =  bal_pb2.BalHeartbeat()
+            obj = bal_pb2.BalHeartbeat()
             obj.device_id = device_id
             rebootStatus = yield self.stub.BalApiHeartbeat(obj)
-            self.log.info('OLT HeartBeat Response Received from', device=device_id, rebootStatus=rebootStatus)
+            self.log.info('OLT HeartBeat Response Received from',
+                          device=device_id, rebootStatus=rebootStatus)
             returnValue(rebootStatus)
         except Exception as e:
             self.log.info('OLT HeartBeat failed', exc=str(e))
+
+    def get_indication_info(self, device_id):
+        while self.olt.running:
+            try:
+                obj = bal_pb2.BalDefault()
+                obj.device_id = str(device_id)
+                bal_ind = self.ind_stub.BalGetIndFromDevice(obj)
+                if bal_ind.ind_present == True:
+                    self.log.info('Indication-received',
+                                  device=device_id, bal_ind=bal_ind)
+                    self.ind_obj.handle_indication_from_bal(bal_ind, self.olt)
+                time.sleep(self.interval)
+            except Exception as e:
+                self.log.info('Failed-to-get-indication-info', exc=str(e))
+        self.log.debug('stop-indication-receive-thread')
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal.proto b/voltha/adapters/asfvolt16_olt/protos/bal.proto
index f1faf2a..8d84b73 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal.proto
@@ -25,6 +25,10 @@
     BalErrno err = 1;
 }
 
+message BalDefault {
+    string device_id = 1;                     /**< device id */
+}
+
 enum BalRebootStatus
 {
     BAL_OLT_UP_AFTER_REBOOT               =  0;
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto b/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
index 7cb1c35..477c663 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
@@ -46,6 +46,7 @@
          BalPacketIeeeOamChannelRx balOamResp                        = 20; // PLOAM response
       }
       string device_id                                               = 21; //Deviced Id
+      uint32 ind_present                                             = 23;
 }
 
 service BalInd {
@@ -71,3 +72,8 @@
       rpc BalPktOmciChannelRxInd(BalIndications) returns(BalErr) {}
       rpc BalPktIeeeOamChannelRxInd(BalIndications) returns(BalErr) {}
 }
+
+service BalGetInd {
+      //Get bal indications from the device
+      rpc BalGetIndFromDevice(BalDefault) returns(BalIndications) {}
+}