import asyncio
import concurrent
from concurrent import futures
from threading import Thread
from typing import Callable
from typing import List
from proto.clients.client_pb2 import Client
from proto.services.request.topicSubscription_pb2 import TopicSubscription
from proto.services.serviceRequest_pb2 import ServiceRequest
from proto.topicData.topicDataRecord_pb2 import TopicDataRecord
from proto.topicData.topicData_pb2 import TopicData
from ubii_service_client import UbiiServiceClient
from ubii_topicdata_client import UbiiTopicDataClient
[docs]class UbiiNetworkClient:
def __init__(self, serviceEndpoint, topicDataEndpoint, clientSpecs, node):
self.serviceEndpoint = serviceEndpoint
self.topicDataEndpoint = topicDataEndpoint
self.serviceClient: UbiiServiceClient = UbiiServiceClient(serviceEndpoint, node)
self.clientSpecification = clientSpecs
self.topicDataClient = None
self.node = node
[docs] async def init(self):
self.clientSpecification = await self.registerAsClient(self.clientSpecification)
self.topicDataClient = UbiiTopicDataClient(self.topicDataEndpoint, self.clientSpecification.id, self.node)
[docs] def callService(self, request: ServiceRequest) -> concurrent.futures.Future:
executor = futures.ThreadPoolExecutor()
future = executor.submit(self.__callServiceDelegate, request)
executor.shutdown(wait=False)
return future
def __callServiceDelegate(self, request: ServiceRequest):
self.node.waitForConnection()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
res = loop.run_until_complete(self.__callServiceDelegateAsync(request))
loop.close()
return res
async def __callServiceDelegateAsync(self, request: ServiceRequest):
try:
return await self.serviceClient.send(request)
except Exception as e:
self.node.events.onServiceCallError(e)
print(e)
return None
[docs] def publish(self, record: TopicDataRecord):
if self.node.isConnected():
self.topicDataClient.publish(record)
else:
t1 = Thread(target=self.__waitPublishDelegate, args=[record])
t1.start()
def __waitPublishDelegate(self, record: TopicDataRecord):
self.node.waitForConnection()
self.topicDataClient.publish(record)
def __publishImmediatelyDelegate(self, record: TopicDataRecord):
self.node.waitForConnection()
asyncio.run(self.__publishImmediatelyDelegateAsync(record))
async def __publishImmediatelyDelegateAsync(self, record: TopicDataRecord):
try:
topicData = TopicData()
topicData.topic_data_record.CopyFrom(record)
await self.topicDataClient.sendTopicData(topicData)
except Exception as e:
print(e)
self.node.events.onPublishError(e)
[docs] async def registerAsClient(self, client: Client) -> Client:
request = ServiceRequest()
request.topic = '/services/client/registration'
request.client.CopyFrom(client)
success = False
reply = None
while not success:
try:
reply = await self.serviceClient.send(request)
success = True
except Exception as e:
print(e)
print('Error while registering client trying again in 15 seconds')
self.node.events.onServiceCallError(e)
await asyncio.sleep(15)
if (reply is None) or (reply.HasField("error")):
print("Error at serviceRequest while registering client")
return None
return reply.client
[docs] def subscribeTopic(self, topic: str, callback: Callable[[TopicDataRecord], None]):
t1 = Thread(target=self.__subscribeTopicDelegate, args=[topic, callback])
t1.start()
def __subscribeTopicDelegate(self, topic: str, callback: Callable[[TopicDataRecord], None]):
self.node.waitForConnection()
if self.topicDataClient.isSubscribed(topic):
self.topicDataClient.addTopicCallback(topic, callback)
return
request = ServiceRequest()
request.topic = '/services/topic_subscription'
topicSubscription = TopicSubscription()
topicSubscription.client_id = self.clientSpecification.id
topicSubscription.subscribe_topics.append(topic)
request.topic_subscription.CopyFrom(topicSubscription)
reply = self.callService(request)
if (reply is None) or (reply.result().HasField("error")):
print("Error while subscribing at Master")
return
self.topicDataClient.addTopicCallback(topic, callback)
[docs] def subscribeRegex(self, regex: str, callback: Callable[[TopicDataRecord], None]):
t1 = Thread(target=self.__subscribeRegexDelegate, args=[regex, callback])
t1.start()
def __subscribeRegexDelegate(self, regex: str, callback: Callable[[TopicDataRecord], None]):
self.node.waitForConnection()
if self.topicDataClient.isSubscribed(regex):
self.topicDataClient.addRegexCallback(regex, callback)
return
request = ServiceRequest()
request.topic = '/services/topic_subscription'
topicSubscription = TopicSubscription()
topicSubscription.client_id = self.clientSpecification.id
topicSubscription.subscribe_topic_regexp.append(regex)
request.topic_subscription.CopyFrom(topicSubscription)
reply = self.callService(request)
if reply is None or reply.result().HasField("error"):
print("Error while subscribing at Master")
return
self.topicDataClient.addRegexCallback(regex, callback)
[docs] def unsubscribe(self, topic: str, callback: Callable[[TopicDataRecord], None]):
t1 = Thread(target=self.__unsubscribeDelegate, args=[topic, callback])
t1.start()
def __unsubscribeDelegate(self, topic: str, callback: Callable[[TopicDataRecord], None]):
self.node.waitForConnection()
self.topicDataClient.removeTopicCallback(topic, callback)
if self.topicDataClient.hasTopicDataCallbacks(topic):
return
self.__unsubscribeTopicMaster([topic])
def __unsubscribeTopicMaster(self, topics: List[str]):
request = ServiceRequest()
request.topic = '/services/topic_subscription'
topicUnsubscription = TopicSubscription()
topicUnsubscription.client_id = self.clientSpecification.id
topicUnsubscription.unsubscribe_topics[:] = topics
request.topic_subscription.CopyFrom(topicUnsubscription)
reply = self.callService(request)
if (reply is None) or (reply.result().HasField("error")):
print("Error while unsubscribing at Master")
return
for topic in topics:
self.topicDataClient.removeTopicDataCallbacks(topic)
[docs] def unsubscribeRegex(self, regex: str, callback: Callable[[TopicDataRecord], None]):
t1 = Thread(target=self.__unsubscribeRegexDelegate, args=[regex, callback])
t1.start()
def __unsubscribeRegexDelegate(self, regex: str, callback: Callable[[TopicDataRecord], None]):
self.node.waitForConnection()
self.topicDataClient.removeRegexCallback(regex, callback)
if self.topicDataClient.hasRegexCallbacks(regex):
return
self.__unsubscribeRegexAtMaster(regex)
def __unsubscribeRegexAtMaster(self, regex: str):
request = ServiceRequest()
request.topic = '/services/topic_subscription'
topicUnsubscription = TopicSubscription()
topicUnsubscription.client_id = self.clientSpecification.id
topicUnsubscription.unsubscribe_topic_regexp.append(regex)
request.topic_subscription.CopyFrom(topicUnsubscription)
reply = self.callService(request)
if (reply is None) or (reply.result().HasField("error")):
print("Error while unsubscribing at Master")
return
self.topicDataClient.removeAllRegexCallbacks(regex)
[docs] def setPublishFrequency(self, frequency):
if not self.topicDataClient is None:
self.topicDataClient.setPublishFrequency(frequency)
print("topicDataClient is not initialized")
[docs] def stopNode(self):
if self.node.isConnected():
request = ServiceRequest()
request.topic = '/services/client/deregistration'
request.client.CopyFrom(self.clientSpecification)
self.callService(request)
if not self.topicDataClient is None:
self.topicDataClient.stopNode()