class OpenRpdDriver(HalDriverClient): """The OpenRPD base driver """ ## __metaclass__ = AddLoggerToClass def __init__(self, drvName, drvDesc, drvVer, supportedMsgType, supportedNotificationMsgs, interestedNotification=None): """ :param drvName: The driver name, such as Generic Driver :param drvDesc: driver for full Cable Labs RPHY support :param drvVer: driver version :param supportedMsgType: will support all RPHY OpenRPD message types :param supportedNotificationMsgs: the supported notification msg types :return: OpenRPD Driver object NOTES: The 'supportedNotificationMsgs' parameter passed to HalDriverClient and then forwarded to HalManager.py, but it seems HalManager.py does NOT do anything with this parameter. Consider remove it from ClientProvision.proto? As of now, we don't need to have this parameter. It just adds confusion! """ if supportedMsgType is None: supportedMsgType = OpenRpdMsgHandler.default_supported_msg_types super(OpenRpdDriver, self).__init__(drvName, drvDesc, drvVer, supportedMsgType, supportedNotificationMsgs, interestedNotification) # update the supported messages self.HalMsgsHandler = { "HalClientRegisterRsp": self.recv_register_msg_cb, "HalClientHelloRsp": self.recvHelloRspMsgCb, "HalConfig": self.recv_cfg_msg_cb, "HalConfigRsp": self.recv_cfg_msg_rsp_cb, "HalNotification": self.recvNotificationCb, } # Handlers for different configuration messages self.hal_config_msg_handlers = { HalConfigMsg.MsgTypeRpdCapabilities: OpenRpdMsgHandler.capabilities_get, HalConfigMsg.MsgTypeDsRfPort: OpenRpdMsgHandler.config_ds_port, HalConfigMsg.MsgTypeDsScQamChannelConfig: OpenRpdMsgHandler.config_dsqam_channel, HalConfigMsg.MsgTypeDsOfdmChannelConfig: OpenRpdMsgHandler.config_dsofdm_channel, HalConfigMsg.MsgTypeDsOfdmProfile: OpenRpdMsgHandler.config_dsofdm_profile, HalConfigMsg.MsgTypeDsRfPortPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeDsScQamChannelPerf: OpenRpdMsgHandler.req_dsqam_channel_status, HalConfigMsg.MsgTypeDsOfdmChannelPerf: OpenRpdMsgHandler.req_dsofdm_channel_status, HalConfigMsg.MsgTypeDsOob551IPerf: OpenRpdMsgHandler.req_oob551_mod_status, HalConfigMsg.MsgTypeDsOob552Perf: OpenRpdMsgHandler.req_oob552_mod_status, HalConfigMsg.MsgTypeNdfPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeUsRfPortPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeUsScQamChannelConfig: OpenRpdMsgHandler.config_usatdma_channel, HalConfigMsg.MsgTypeUsOfdmaChannelConfig: OpenRpdMsgHandler.config_usofdma_channel, HalConfigMsg.MsgTypeUsOfdmaInitialRangingIuc: OpenRpdMsgHandler.config_dummy, HalConfigMsg.MsgTypeUsOfdmaFineRangingIuc: OpenRpdMsgHandler.config_dummy, HalConfigMsg.MsgTypeUsOfdmaDataRangingIuc: OpenRpdMsgHandler.config_dummy, HalConfigMsg.MsgTypeUsOfdmaDataIuc: OpenRpdMsgHandler.config_dummy, HalConfigMsg.MsgTypeUsOfdmaSubcarrierCfgState: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeUsScQamChannelPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeUsOfdmaChannelPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeUsOob551IPerf: OpenRpdMsgHandler.req_oob551_demod_status, HalConfigMsg.MsgTypeUsOob552Perf: OpenRpdMsgHandler.req_oob552_demod_status, HalConfigMsg.MsgTypeNdrPerf: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeSidQos: OpenRpdMsgHandler.config_sid_qos, # L2TPv3 messages HalConfigMsg.MsgTypeL2tpv3CapabilityQuery: OpenRpdMsgHandler.capabilities_get, HalConfigMsg.MsgTypeL2tpv3SessionReqNone: OpenRpdMsgHandler.req_depi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqDsOfdm: OpenRpdMsgHandler.req_depi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqDsOfdmPlc: OpenRpdMsgHandler.req_depi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqDsScqam: OpenRpdMsgHandler.req_depi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqUsAtdma: OpenRpdMsgHandler.req_uepi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqUsOfdma: OpenRpdMsgHandler.req_uepi_pw, HalConfigMsg.MsgTypeL2tpv3SessionReqScte551Fwd: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeL2tpv3SessionReqScte551Ret: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeL2tpv3SessionReqScte552Fwd: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeL2tpv3SessionReqScte552Ret: OpenRpdMsgHandler.req_dummy, HalConfigMsg.MsgTypeL2tpv3SessionReqNdf: OpenRpdMsgHandler.req_ndf, HalConfigMsg.MsgTypeL2tpv3SessionReqNdr: OpenRpdMsgHandler.req_ndr, # Ptp # HalConfigMsg.MsgTypeRdtiConfig: config_docsis_timer HalConfigMsg.MsgTypeL2tpv3CinIfAssignment: OpenRpdMsgHandler.cin_if_assign, HalConfigMsg.MsgTypeL2tpv3LcceIdAssignment: OpenRpdMsgHandler.lcce_id_assign, # VspAvpQuery HalConfigMsg.MsgTypeVspAvpExchange: OpenRpdMsgHandler.vsp_avp_handler, # RcpVendorSpecificTlv HalConfigMsg.MsgTypeRcpVendorSpecific: OpenRpdMsgHandler.vsp_tlv_handler, } self.disconnected = True self.dispatcher = Dispatcher() # setup the logging self.logger = OpenRpdMsgHandler.get_msg_handler_logger() self.logger.info("OpenRPD Driver Initialized") self.seqNum = 0 # start modeled from HalPtpDriver.py def start(self, simulate_mode=False): """start poll the transport socket :return: """ self.logger.info("Setup the Hal Transport connection...") self.connectionSetup() self.logger.info("Connection setup done...") self.logger.info("Register the driver with the Hal manager...") self.register(self.drvID) self.logger.info("End of register...") self.dispatcher.loop() def recv_register_msg_cb(self, cfg): """the callback handler for the configuration message Modified from base class by registering the sockets with dispatcher. :param cfg: the configuration message received from the Hal :return: """ # self.logger.debug("Recv a Message from the Hal:" % str(cfg.msg)) if cfg.msg.Rsp.Status != HalCommon_pb2.SUCCESS: self.logger.error("Cannot register to Hal, reason[%s]" % cfg.msg.Rsp.ErrorDescription) return self.drvID = cfg.msg.ClientID # Setup the push and pull connection self.pullPath = cfg.msg.PathFromHalToClient self.pushPath = cfg.msg.PathFromClientToHal # get the index of the path index = self._getIndexFromPath() if index == -1: self.logger.error("Cannot get index from the path [%s]" % self.pushPath) return if self.index == -1: self.index = index self.pushSock = HalTransport( HalTransport.HalTransportClientAgentPull, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPushMode, disconnectHandlerCb=self.connectionDisconnectCb) self.pullSock = HalTransport( HalTransport.HalTransportClientAgentPush, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPullMode, disconnectHandlerCb=self.connectionDisconnectCb) # register driver's sockets with the dispatcher self.dispatcher.fd_register(self.pullSock.socket, zmq.POLLIN, self.openrpd_drv_hal_cb) self.dispatcher.fd_register(self.pushSock.monitor, zmq.POLLIN, self.openrpd_drv_hal_cb) self.dispatcher.fd_register(self.pullSock.monitor, zmq.POLLIN, self.openrpd_drv_hal_cb) # send Hello To Hal self.sayHelloToHal() if (None is not self.interestedNotification): self.sendInterestedNotifications(self.interestedNotification) self.disconnected = False return def connectionSetup(self): """Create the connection to the mgr :return: """ self.logger.info("Create the connection to the mgr....") # Create a connection to Hal driver mgr self.mgrConnection = HalTransport( HalTransport.HalTransportClientMgr, HalTransport.HalClientMode, disconnectHandlerCb=self.disconnectCb) self.mgrConnection.connects() self.HalMsgsHandler[ self.mgrConnection.socket] = self.recv_register_msg_cb # register the mgr socket with the dispatcher self.dispatcher.fd_register(self.mgrConnection.socket, zmq.POLLIN, self.openrpd_drv_hal_cb) self.dispatcher.fd_register(self.mgrConnection.monitor, zmq.POLLIN, self.openrpd_drv_hal_cb) def connection_cleanup(self): """Close the connection to the mgr :return: """ if self.disconnected: self.logger.debug("A previous event has been processed, skip it!") return if self.mgrConnection is not None: self.dispatcher.fd_unregister(self.mgrConnection.socket) self.dispatcher.fd_unregister(self.mgrConnection.monitor) self.mgrConnection.socket.disable_monitor() self.mgrConnection.monitor.close() self.mgrConnection.socket.close() if self.pullSock is not None: self.dispatcher.fd_unregister(self.pullSock.socket) self.dispatcher.fd_unregister(self.pullSock.monitor) self.pullSock.socket.disable_monitor() self.pullSock.monitor.close() self.pullSock.socket.close() if self.pushSock is not None: self.dispatcher.fd_unregister(self.pushSock.monitor) self.pushSock.socket.disable_monitor() self.pushSock.monitor.close() self.pushSock.socket.close() self.disconnected = True def disconnectCb(self, msg): """A disconnect condition has been detected, clean up the connection and then reconnect and re-register with the Hal. :param msg: :return: """ self.logger.error("Detected disconnected condition") if self.disconnected: self.logger.info("A previous event has been processed, skip it!") return self.logger.info("Detected disconnected, registering again") # clean up the push and pull socket self.dispatcher.fd_unregister(self.mgrConnection.socket) self.dispatcher.fd_unregister(self.mgrConnection.monitor) self.mgrConnection.monitor.close() self.mgrConnection.close() # re-register the message self.connectionSetup() # The zmq lower part will handle the reconnect self.register(self.drvID) self.disconnected = True def openrpd_drv_hal_cb(self, sock, mask): self.logger.debug("Driver received hal cb event") if self.pushSock is not None and sock == self.pushSock.monitor: self.pushSock.monitorHandler(recv_monitor_message(sock)) return if self.pullSock is not None and sock == self.pullSock.monitor: self.pullSock.monitorHandler(recv_monitor_message(sock)) return if sock == self.mgrConnection.monitor: self.mgrConnection.monitorHandler(recv_monitor_message(sock)) return while sock.getsockopt(zmq.EVENTS) and zmq.POLLIN: try: bin = sock.recv(flags=zmq.NOBLOCK) msg = HalMessage.DeSerialize(bin) self.logger.debug("Got a zmq msg:%s type:%s" % (msg.msg, msg.type)) if msg.type in self.HalMsgsHandler: handler = self.HalMsgsHandler[msg.type] handler(msg) except zmq.ZMQError as e: self.logger.debug( "Got an error when trying with nonblock read:" + str(e)) break except Exception as e: self.logger.error("Got an un-expected error:%s", str(e)) break def recvNotificationCb(self, ntf): """Receive a notification message from the HAL. :param ntf: :return: """ try: handler = self.hal_config_msg_handlers[ntf.msg.HalNotificationType] self.logger.info("Receive a interest notification message:" + str(ntf.msg)) if not isinstance(ntf, HalMessage): raise AttributeError("Invalid HAL message passed") ntf = handler(ntf) if None is not ntf: self.send_cfg_msg(HalConfigMsg.MsgTypeVspAvpExchange, ntf.msg.HalNotificationPayLoad) else: self.logger.info("Notification message return is None") except Exception as e: self.logger.error("Got an error:%s, the ntf msg:%s", str(e), ntf.msg) def send_cfg_msg(self, cfgMsgType, payload): msg = HalMessage("HalConfig", SrcClientID=self.drvID, CfgMsgType=cfgMsgType, SeqNum=self.seqNum, CfgMsgPayload=payload) self.logger.debug("sending config - type: %d, msg: %s" % (cfgMsgType, msg)) self.pushSock.send(msg.Serialize()) self.seqNum += 1 return def send_cfg_rsp_msg(self, cfg): """The configuration response routine :param cfg: The original configuration message :return: """ result = HalCommon_pb2.SUCCESS cfgMsg = cfg.msg l2tpcfgmsgType = (HalConfigMsg.MsgTypeL2tpv3SessionReqDsOfdm, HalConfigMsg.MsgTypeL2tpv3SessionReqDsOfdmPlc, HalConfigMsg.MsgTypeL2tpv3SessionReqDsScqam, HalConfigMsg.MsgTypeL2tpv3SessionReqUsAtdma, HalConfigMsg.MsgTypeL2tpv3SessionReqUsOfdma, HalConfigMsg.MsgTypeL2tpv3SessionReqScte551Fwd, HalConfigMsg.MsgTypeL2tpv3SessionReqScte551Ret, HalConfigMsg.MsgTypeL2tpv3SessionReqScte552Fwd, HalConfigMsg.MsgTypeL2tpv3SessionReqScte552Ret, HalConfigMsg.MsgTypeL2tpv3SessionReqNdf, HalConfigMsg.MsgTypeL2tpv3SessionReqNdr, HalConfigMsg.MsgTypeL2tpv3CinIfAssignment, HalConfigMsg.MsgTypeL2tpv3LcceIdAssignment) if cfgMsg.CfgMsgType in l2tpcfgmsgType: rsp = L2tpv3Hal_pb2.t_l2tpSessionRsp() req = L2tpv3Hal_pb2.t_l2tpSessionReq() req.ParseFromString(cfgMsg.CfgMsgPayload) # fill session_selector rsp.session_selector.local_session_id = req.session_selector.local_session_id rsp.session_selector.remote_session_id = req.session_selector.remote_session_id rsp.session_selector.local_ip = req.session_selector.local_ip rsp.session_selector.remote_ip = req.session_selector.remote_ip rsp.result = True elif (cfgMsg.CfgMsgType == HalConfigMsg.MsgTypeVspAvpExchange): rsp = L2tpv3VspAvp_pb2.t_l2tpVspAvpMsg() rsp.ParseFromString(cfgMsg.CfgMsgPayload) self.logger.debug( "vsp_avp_handler re-parse srcClientID: %s, Seq num: %d, " "op: %d, vid %d, attr %d, strVal %s" % (cfg.msg.SrcClientID, cfg.msg.SeqNum, rsp.oper, rsp.vendorId, rsp.attrType, rsp.attrValBuf)) if rsp.rspCode == L2tpv3VspAvp_pb2.t_l2tpVspAvpMsg( ).VSP_AVP_STATUS_FAILURE: # send HalConfigRsp with failure status if OpenRPD driver can't handle this. result = HalCommon_pb2.FAILED elif (cfgMsg.CfgMsgType == HalConfigMsg.MsgTypeRcpVendorSpecific): rsp = t_RcpMessage() rsp.ParseFromString(cfgMsg.CfgMsgPayload) self.logger.debug("send_cfg_rsp_msg payload: %s, result: %d" % (rsp.RpdDataMessage.RpdData, rsp.RcpDataResult)) else: rsp = t_RcpMessage() rsp.ParseFromString(cfgMsg.CfgMsgPayload) self.logger.debug("send_cfg_rsp_msg payload: %s" % rsp.RpdDataMessage.RpdData) rsp.RcpDataResult = t_RcpMessage.RCP_RESULT_OK payload = rsp.SerializeToString() self.logger.debug("cfg response srcClientID: %s, Seq num: %d" % (cfgMsg.SrcClientID, cfgMsg.SeqNum)) msg = HalMessage("HalConfigRsp", SrcClientID=cfgMsg.SrcClientID, SeqNum=cfgMsg.SeqNum, Rsp={ "Status": HalCommon_pb2.SUCCESS, "ErrorDescription": "" }, CfgMsgType=cfgMsg.CfgMsgType, CfgMsgPayload=payload) self.logger.debug("sending cfg response - type: %d, msg: %s" % (cfgMsg.CfgMsgType, msg)) self.pushSock.send(msg.Serialize()) def recv_cfg_msg_rsp_cb(self, cfg): cfgMsg = cfg.msg self.logger.debug("receive cfg response - type: %d" % (cfgMsg.CfgMsgType)) pass def recv_cfg_msg_cb(self, cfg): """Receive a configuration message from the Hal, processing it :param cfg: :return: """ try: handler = self.hal_config_msg_handlers[cfg.msg.CfgMsgType] self.logger.info("Received a cfg message type: %d", cfg.msg.CfgMsgType) if not isinstance(cfg, HalMessage): raise AttributeError("Invalid HAL message passed") cfg = handler(cfg) self.send_cfg_rsp_msg(cfg) except Exception as e: self.logger.error("Got an error:%s, the cfg msg:%s", str(e), cfg.msg) def cleanup_sockets(self): for fd in self.fd_to_socket: sock = self.fd_to_socket[fd] self.poller.unregister(sock) sock.close() self.fd_to_socket.clear()
class HalDriverClient(object): """The Driver Client for Hal.""" __metaclass__ = AddLoggerToClass def __init__(self, drvName, drvDesc, drvVer, supportedMsgType, supportedNotificationMsgs, interestedNotification=None): """Init. :param drvName: The driver name, such as BCM3160 Driver :param drvDesc: A brief description about this driver, such as the driver main functionality description :param drvVer: Driver specific version, such as 1.0.1 :param supportedMsgType: a tuple or list for the driver supported msg types, the form will be (1, 2, 456, 10) :param supportedNotificationMsgs: the driver supported notification msg types the form will be (1, 3, 4) :return: HalDriverClient object """ # sanity check the input args if not isinstance(drvName, str) or not isinstance( drvDesc, str) or not isinstance(drvVer, str): raise HalDriverClientError( "Driver name/desc/version should be a str type") if not isinstance(supportedMsgType, tuple) and not isinstance( supportedMsgType, list): raise HalDriverClientError( "supportedMsgType should be a tuple or list") if (supportedNotificationMsgs is not None) and ( not isinstance(supportedNotificationMsgs, list) and not isinstance(supportedNotificationMsgs, tuple)): raise HalDriverClientError( "supportedNotificationMsgs is allowed none or tuple or list") self.drvname = drvName self.drvDesc = drvDesc self.drvVer = drvVer if None is not interestedNotification: self.interestedNotification = list(interestedNotification) else: self.interestedNotification = None self.supportedMsgType = list(supportedMsgType) if supportedNotificationMsgs is not None: self.supportedNotificationMsgs = list(supportedNotificationMsgs) else: self.supportedNotificationMsgs = None self.recvNtf = 0 self.pollTimeout = 1000 # update the supported messages self.HalMsgsHandler = { "HalClientRegisterRsp": self.recvRegisterMsgCb, "HalClientHelloRsp": self.recvHelloRspMsgCb, "HalConfig": self.recvCfgMsgCb, "HalConfigRsp": self.recvCfgMsgRspCb, "HalClientInterestNotificationCfgRsp": self.sendInterestedNotificationsRspCb, "HalNotification": self.recvNotificationCb, } self.drvID = None self.mgrConnection = None self.pushSock = None self.pullSock = None self.pullPath = None self.pushPath = None self.disconnected = True self.poller = None self.index = -1 self.seqNum = 0 def start(self): """Start polling the transport socket. :return: """ self.logger.debug("Start the driver client poll...") self.connectionSetup() self.register(self.drvID) lastTimeout = time() while True: # Todo we should support quit flag? socks = self.poller.poll(self.pollTimeout) if time() - lastTimeout > self.pollTimeout / 1000: lastTimeout = time() # self.logger.debug("Got a timeout event") if self.recvNtf: rcp_msg = t_RcpMessage() rcp_msg.RpdDataMessage.RpdDataOperation = t_RpdDataMessage.RPD_CFG_READ rcp_msg.RcpMessageType = t_RcpMessage.RPD_CONFIGURATION rcp_msg.RpdDataMessage.RpdData.CopyFrom(config()) payload = rcp_msg.SerializeToString() self.sendCfgMsg(1025, payload) self.recvNtf -= 1 if not socks: continue for sock in socks: if self.pushSock is not None and sock == self.pushSock.monitor: self.pushSock.monitorHandler(recv_monitor_message(sock)) continue if self.pullSock is not None and sock == self.pullSock.monitor: self.pullSock.monitorHandler(recv_monitor_message(sock)) continue if sock == self.mgrConnection.monitor: self.mgrConnection.monitorHandler( recv_monitor_message(sock)) continue if socks[sock] == HalPoller.POLLIN: try: bin = sock.recv(flags=zmq.NOBLOCK) msg = HalMessage.DeSerialize(bin) self.logger.debug("Got a zmq msg:%s" % msg.msg) if msg.type in self.HalMsgsHandler: handler = self.HalMsgsHandler[msg.type] handler(msg) else: self.logger.warn("Unsupported msg type:%s" % msg.type) except zmq.ZMQError as e: self.logger.debug( "Geting an error when trying with nonblock read:" + str(e)) except Exception as e: self.logger.debug("Geting an error:" + str(e)) continue def connectionSetup(self): """Create the connection to the mgr and setup the poller. :return: """ self.logger.debug("Create the connection to the mgr....") # Create a connection to Hal driver mgr self.mgrConnection = HalTransport(HalTransport.HalTransportClientMgr, HalTransport.HalClientMode) self.mgrConnection.connects() self.HalMsgsHandler[self.mgrConnection.socket] = self.recvRegisterMsgCb # create the poller if self.poller is None: self.poller = HalPoller() # register the mgr socket self.poller.register(self.mgrConnection.socket) self.poller.register(self.mgrConnection.monitor) def register(self, driverID): """Send a register message to Hal and get the client ID from the Hal. :return: """ if driverID is None: registerMsg = HalMessage( "HalClientRegister", ClientName=self.drvname, ClientDescription=self.drvDesc, ClientVersion=self.drvVer, ClientSupportedMessages=self.supportedMsgType, ClientSupportedNotificationMessages=self. supportedNotificationMsgs) else: registerMsg = HalMessage( "HalClientRegister", ClientName=self.drvname, ClientDescription=self.drvDesc, ClientVersion=self.drvVer, ClientSupportedMessages=self.supportedMsgType, ClientSupportedNotificationMessages=self. supportedNotificationMsgs, ClientID=driverID) if self.mgrConnection is None: errMsg = "Cannot send the register since the mgr connection is not setup" self.logger.error(errMsg) raise HalDriverClientError(errMsg) self.logger.debug("Send the register msg to Hal...") self.mgrConnection.send(registerMsg.Serialize()) def send(self, msg): if self.pushSock: self.pushSock.send(msg) else: self.logger.warning(" ".join([ str(self.drvname), str(self.drvID), ":Cannot send the msg since the push socket is none" ])) def sayHelloToHal(self): """Send a hello message to verify the agent path is correct. :return: """ self.logger.debug(" ".join([ str(self.drvname), str(self.drvID), ":Send a Hello message to Hal" ])) helloMsg = HalMessage("HalClientHello", ClientID=self.drvID) self.send(helloMsg.Serialize()) def recvHelloRspMsgCb(self, hello): """Call back for Hello Message. :param hello: :return: """ self.logger.debug("Recv a hello message") def sendInterestedNotifications(self, notifications): """Send the notifications to the HAL. :param notifications: :return: """ self.logger.debug( "Send a Interested notification configuration msg to HAL") if notifications is not None and not isinstance( notifications, tuple) and not isinstance(notifications, list): self.logger.error( "Cannot set an notification with wrong type, you can pass a tuple or list to it " ) return configMsg = HalMessage("HalClientInterestNotificationCfg", ClientID=self.drvID, ClientNotificationMessages=notifications) self.mgrConnection.send(configMsg.Serialize()) def sendInterestedNotificationsRspCb(self, rsp): """Receive a response message from the HAL for the notification rsp callback. :param rsp: :return: """ self.logger.debug("Receive a interest notification response message:" + str(rsp.msg)) def recvNotificationCb(self, ntf): """Receive a notification message from the HAL. :param ntf: :return: """ self.logger.debug("Receive a interest notification message:" + str(ntf.msg)) self.recvNtf += 1 def recvCfgMsgCb(self, cfg): """Receive a configuration message from the Hal, processing it. :param cfg: :return: """ self.logger.debug( "Recv a configuration message, send a fake rsp to it") self.sendCfgRspMsg(cfg) def recvCfgMsgRspCb(self, cfg): """Receive a configuration response message from the Hal, processing it. :param cfg: :return: """ self.logger.debug("Recv a configuration response message:" + str(cfg.msg)) def connectionSetupCb(self): pass def connectionDisconnectCb(self, msg): """The connection has been detected disconnected , register it again. :param msg: :return: """ if self.disconnected: self.logger.debug("A previous event has been processed, skip it!") return self.logger.debug("Detected disconnected, register again") # clean up the push and pull socket # self.poller.unregister(self.pullSock.socket) self.poller.unregister(self.mgrConnection.socket) self.poller.unregister(self.mgrConnection.monitor) self.mgrConnection.socket.disable_monitor() self.mgrConnection.monitor.close() self.mgrConnection.close() # re-register the message self.connectionSetup() self.register(self.drvID) # The zmq lower part will handle the reconnect self.disconnected = True def sendNotificationMsg(self, notificationType, notificationPayload): """Send a notification to Hal. :param notificationType: The notification type, the client must declare the notification type to Hal first :param notificationPayload: the string payload, Hal will not touch this part :return: """ self.logger.debug("send a a notification message to Hal") notfication = HalMessage("HalNotification", ClientID=self.drvID, HalNotificationType=notificationType, HalNotificationPayLoad=notificationPayload) self.send(notfication.Serialize()) def sendCfgMsg(self, cfgMsgType, cfgMsgContent): """The configutaion response routine, the driver implementor should fill sth into this function. :param cfg: The original configuration message :return: """ self.logger.debug("Send a config message to HAL: %r", cfgMsgContent) if self.disconnected: self.logger.warn( "The client is on disconencted state, skip to send the message." ) return if cfgMsgContent is None or not isinstance(cfgMsgContent, str): self.logger.error( "Cannot send a None or incorrect type to HAL, str is required for msg" ) return msg = HalMessage("HalConfig", SrcClientID=self.drvID, SeqNum=self.seqNum, CfgMsgType=cfgMsgType, CfgMsgPayload=cfgMsgContent) self._sendMsg(msg.Serialize()) seq = self.seqNum self.seqNum += 1 return seq def sendCfgRspMsg(self, cfg, rsp=None): """The configuration response routine, the driver implementor should fill sth into this function. :param cfg: The original configuration message :param rsp: respond :return: """ cfgMsg = cfg.msg if rsp == None: rsp = {"Status": HalCommon_pb2.SUCCESS, "ErrorDescription": ""} msg = HalMessage("HalConfigRsp", SrcClientID=cfgMsg.SrcClientID, SeqNum=cfgMsg.SeqNum, Rsp=rsp, CfgMsgType=cfgMsg.CfgMsgType, CfgMsgPayload=cfgMsg.CfgMsgPayload) if None is not self.pushSock: self.pushSock.send(msg.Serialize()) def recvRegisterMsgCb(self, cfg): """The callback handler for the configuration message. :param cfg: the configuration message received frm the Hal :return: """ # self.logger.debug("Recv a Message from the Hal:" % str(cfg.msg)) if cfg.msg.Rsp.Status != HalCommon_pb2.SUCCESS: self.logger.error("Cannot register to Hal, reason[%s]" % cfg.msg.Rsp.ErrorDescription) return self.drvID = cfg.msg.ClientID # Setup the push and pull connection self.pullPath = cfg.msg.PathFromHalToClient self.pushPath = cfg.msg.PathFromClientToHal # get the index of the path index = self._getIndexFromPath() if index == -1: self.logger.error("Cannot get index from the path [%s]" % self.pushPath) return if self.index == -1: self.index = index self.pushSock = HalTransport( HalTransport.HalTransportClientAgentPull, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPushMode, disconnectHandlerCb=self.connectionDisconnectCb) self.pullSock = HalTransport( HalTransport.HalTransportClientAgentPush, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPullMode, disconnectHandlerCb=self.connectionDisconnectCb) # register to the poller self.poller.register(self.pushSock.monitor) self.poller.register(self.pullSock.monitor) self.poller.register(self.pullSock.socket) # send Hello To Hal self.sayHelloToHal() if None is not self.interestedNotification: self.sendInterestedNotifications(self.interestedNotification) self.disconnected = False return def _getIndexFromPath(self): rePattern = r"/(\d+)/" ret = re.search(rePattern, self.pushPath) if ret is not None: digitStr = ret.group(1) return int(digitStr) return -1 def _sendMsg(self, msg): if self.pushSock: self.pushSock.send(msg) else: self.logger.error( "Cannot send the msg since the push socket is NULL")
# 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. import zmq import hexdump from rpd.hal.src.transport.HalTransport import HalTransport from rpd.hal.src.msg.HalMessage import HalMessage from rpd.hal.src.msg import HalCommon_pb2 from rpd.hal.src.msg.HalOperation_pb2 import HalNotification from rpd.hal.src.msg.ClientProvision_pb2 import HalClientRegister if __name__ == "__main__": trans = HalTransport(HalTransport.HalTransportClientMgr, HalTransport.HalClientMode) trans.connects() registerMsg = HalClientRegister() registerMsg.MsgType = "HalClientRegister" registerMsg.ClientName = "Test" registerMsg.ClientDescription = "This is a test msg" registerMsg.ClientVersion = "1.0" registerMsg.ClientSupportedMessages.append(1) registerMsg.ClientSupportedMessages.append(123) registerMsg.ClientSupportedNotificationMessages.append(11) registerMsg.ClientSupportedNotificationMessages.append(12) strMsg = registerMsg.SerializeToString() # dump the message hexdump.hexdump(strMsg)
class HalPtpDriver(HalDriverClient): """The Driver Client for Hal.""" SYNC = "ALIGNED" LOS = "LOSS OF SYNC" __metaclass__ = AddLoggerToClass def __init__(self, drvName, drvDesc, drvVer, supportedMsgType, supportedNotificationMsgs, logConfigurePath=None): """Init. :param drvName: The driver name, such as BCM3160 Driver :param drvDesc: A brief description about this driver, such as the driver main functionality description :param drvVer: Driver specific version, such as 1.0.1 :param supportedMsgType: a tuple or list for the driver supported msg types, the form will be (1, 2, 456, 10) :param supportedNotificationMsgs: the driver supported notification msg types the form will be (1, 3, 4) :return: HalDriverClient object """ super(HalPtpDriver, self).__init__(drvName, drvDesc, drvVer, supportedMsgType, supportedNotificationMsgs, logConfigurePath) # update the supported messages self.HalMsgsHandler = { "HalClientRegisterRsp": self.recvRegisterMsgCb, "HalClientHelloRsp": self.recvHelloRspMsgCb, "HalConfig": self.recvCfgMsgCb, } self.HalConfigMsgHandlers = { MsgTypePtpStatusGet: self.ptp_status_get, MsgTypeRdtiConfig: self.config_rdti, } self.ptpStatus = self.LOS self.ptpNewStatus = self.LOS self.dispatcher = Dispatcher() def start(self, simulate_mode=False): """Start polling the transport socket. :return: """ self.logger.info("Start the driver client poll...") self.connectionSetup() self.logger.info("Connection setup done...") self.logger.info("Begin register...") self.register(self.drvID) self.logger.info("End of register...") self.dispatcher.loop() def ptpdrv_hal_cb(self, sock, mask): self.logger.debug("Receive prp drv event") if self.pushSock is not None and sock == self.pushSock.monitor: self.pushSock.monitorHandler(recv_monitor_message(sock)) return if self.pullSock is not None and sock == self.pullSock.monitor: self.pullSock.monitorHandler(recv_monitor_message(sock)) return if sock == self.mgrConnection.monitor: self.mgrConnection.monitorHandler(recv_monitor_message(sock)) return while sock.getsockopt(zmq.EVENTS) and zmq.POLLIN: try: bin = sock.recv(flags=zmq.NOBLOCK) msg = HalMessage.DeSerialize(bin) self.logger.debug("Got a zmq msg:%s type:%s" % (msg.msg, msg.type)) if msg.type in self.HalMsgsHandler: handler = self.HalMsgsHandler[msg.type] handler(msg) except zmq.ZMQError as e: self.logger.debug( "Got an error when trying with nonblock read:" + str(e)) break except Exception as e: self.logger.warning("Got an un-expected error:%s", str(e)) break def register(self, DriverID): """Send a register message to Hal and get the client ID from the Hal. :return: """ if DriverID is None: registerMsg = HalMessage( "HalClientRegister", ClientName=self.drvname, ClientDescription=self.drvDesc, ClientVersion=self.drvVer, ClientSupportedMessages=self.supportedMsgType, ClientSupportedNotificationMessages=self. supportedNotificationMsgs) else: registerMsg = HalMessage( "HalClientRegister", ClientName=self.drvname, ClientDescription=self.drvDesc, ClientVersion=self.drvVer, ClientSupportedMessages=self.supportedMsgType, ClientSupportedNotificationMessages=self. supportedNotificationMsgs, ClientID=DriverID) if self.mgrConnection is None: errMsg = "Cannot send the register since the mgr connection is not setup" self.logger.error(errMsg) raise HalDriverClientError(errMsg) self.logger.debug("Send the register msg to Hal...") self.mgrConnection.send(registerMsg.Serialize()) def recvRegisterMsgCb(self, cfg): """The callback handler for the configuration message. :param cfg: the configuration message received frm the Hal :return: """ self.logger.debug("Recv a register rsp Message from the Hal: %s" % cfg.msg) if cfg.msg.Rsp.Status != HalCommon_pb2.SUCCESS: self.logger.error("Cannot register to Hal, reason[%s]", cfg.msg.Rsp.ErrorDescription) return self.drvID = cfg.msg.ClientID # Setup the push and pull connection self.pullPath = cfg.msg.PathFromHalToClient self.pushPath = cfg.msg.PathFromClientToHal # get the index of the path index = self._getIndexFromPath() if index == -1: self.logger.error("Cannot get index from the path [%s]" % self.pushPath) return if self.index == -1: self.index = index self.pushSock = HalTransport( HalTransport.HalTransportClientAgentPull, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPushMode, disconnectHandlerCb=self.connectionDisconnectCb) self.pullSock = HalTransport( HalTransport.HalTransportClientAgentPush, HalTransport.HalClientMode, index=index, socketMode=HalTransport.HalSocketPullMode, disconnectHandlerCb=self.connectionDisconnectCb) # register to the poller self.dispatcher.fd_register(self.pullSock.socket, zmq.POLLIN, self.ptpdrv_hal_cb) self.dispatcher.fd_register(self.pushSock.monitor, zmq.POLLIN, self.ptpdrv_hal_cb) self.dispatcher.fd_register(self.pullSock.monitor, zmq.POLLIN, self.ptpdrv_hal_cb) # send Hello To Hal self.sayHelloToHal() if self.interestedNotification is not None: self.sendInterestedNotifications(self.interestedNotification) self.disconnected = False return def connectionSetup(self): """Create the connection to the mgr and setup the poller. :return: """ self.logger.info("Create the connection to the mgr....") # Create a connection to Hal driver mgr # Create a connection to Hal driver mgr self.mgrConnection = HalTransport( HalTransport.HalTransportClientMgr, HalTransport.HalClientMode, disconnectHandlerCb=self.connectionDisconnectCb) self.mgrConnection.connects() self.HalMsgsHandler[self.mgrConnection.socket] = self.recvRegisterMsgCb self.HalMsgsHandler[self.mgrConnection.socket] = self.recvRegisterMsgCb # register the mgr socket self.dispatcher.fd_register(self.mgrConnection.socket, zmq.POLLIN, self.ptpdrv_hal_cb) self.dispatcher.fd_register(self.mgrConnection.monitor, zmq.POLLIN, self.ptpdrv_hal_cb) def connection_cleanup(self): """Close the connection to the mgr. :return: """ if self.disconnected: self.logger.debug("A previous event has been processed, skip it!") return if self.mgrConnection is not None: self.dispatcher.fd_unregister(self.mgrConnection.socket) self.dispatcher.fd_unregister(self.mgrConnection.monitor) self.mgrConnection.socket.disable_monitor() self.mgrConnection.monitor.close() self.mgrConnection.socket.close() if self.pullSock is not None: self.dispatcher.fd_unregister(self.pullSock.socket) self.dispatcher.fd_unregister(self.pullSock.monitor) self.pullSock.socket.disable_monitor() self.pullSock.monitor.close() self.pullSock.socket.close() if self.pushSock is not None: self.dispatcher.fd_unregister(self.pushSock.monitor) self.pushSock.socket.disable_monitor() self.pushSock.monitor.close() self.pushSock.socket.close() self.disconnected = True def connectionDisconnectCb(self, msg): """TODO: confusing comment here. Need clarification. The connection has been detected disconnected , register it again We have reconenct, we have to assure the regiter message is received by the HAL :param msg: :return: """ if self.disconnected: self.logger.info("A previous event has been processed, skip it!") return self.logger.info("Detected disconnected, register again") # clean up the push and pull socket # self.poller.unregister(self.pullSock.socket) self.dispatcher.fd_unregister(self.mgrConnection.socket) self.dispatcher.fd_unregister(self.mgrConnection.monitor) self.mgrConnection.monitor.close() self.mgrConnection.close() # re-register the message self.connectionSetup() self.register(self.drvID) # The zmq lower part will handle the reconnect self.disconnected = True def recvHelloRspMsgCb(self, hello): """Call back for Hello Message. :param hello: :return: """ self.logger.debug("Recv a hello message") def recvCfgMsgCb(self, cfg): """Receive a configuration message from the Hal, processing it. :param cfg: :return: """ try: handler = self.HalConfigMsgHandlers[cfg.msg.CfgMsgType] handler(cfg) except Exception as e: self.logger.error("Got an error:%s, the cfg msg:%s", str(e), cfg.msg) def ptp_status_get(self, cfg): cfg.msg.CfgMsgPayload = self.SYNC self.sendCfgRspMsg(cfg) def config_rdti(self, cfg): rdti_config_data = t_RcpMessage() rdti_config_data.ParseFromString(cfg.msg.CfgMsgPayload) rdti_config_data.RcpDataResult = t_RcpMessage.RCP_RESULT_OK cfg.msg.CfgMsgPayload = rdti_config_data.SerializeToString() self.logger.debug("Recv ptp configuration message, %s" % rdti_config_data) self.sendCfgRspMsg(cfg) def sendCfgRspMsg(self, cfg, rsp=None): """The configuration response routine, the driver implementor should fill sth into this function. :param cfg: The original configuration message :return: """ cfgMsg = cfg.msg rsp = {"Status": HalCommon_pb2.SUCCESS, "ErrorDescription": ""} msg = HalMessage("HalConfigRsp", SrcClientID=cfgMsg.SrcClientID, SeqNum=cfgMsg.SeqNum, Rsp=rsp, CfgMsgType=cfgMsg.CfgMsgType, CfgMsgPayload=cfgMsg.CfgMsgPayload) self.pushSock.send(msg.Serialize())