Source code for src.ubii_topicdata_client

import re
import threading
import time
from threading import Thread
from typing import Callable

import websockets
from proto.topicData.topicDataRecord_pb2 import TopicDataRecord, TopicDataRecordList
from websockets.sync.client import connect

from proto.topicData.topicData_pb2 import TopicData


[docs]class UbiiTopicDataClient: def __init__(self, endpoint, clientID, node): self.ws = connect(endpoint + '?clientID=' + clientID) self.endpoint = endpoint + '?clientID=' + clientID print("UbiiTopicDataClient.endpoint: ", self.endpoint) self.topicCallbacks: dict[str, list[Callable[[TopicDataRecord], None]]] = {} self.regexCallbacks: dict[str, list[Callable[[TopicDataRecord], None]]] = {} self.topicsToPublish = {} self.lockTopics = threading.Lock() self.lockRegex = threading.Lock() self.lockTopicsToPublish = threading.Lock() self.publishFrequency = 0.3 self.running = True self.node = node t1 = Thread(target=self.__writeSocket, args=[]) t1.daemon = True t1.start() t2 = Thread(target=self.__readSocket, args=[]) t2.daemon = True t2.start()
[docs] def publish(self, topicDataRecord: TopicDataRecord): with self.lockTopicsToPublish: self.topicsToPublish[topicDataRecord.topic] = topicDataRecord
def __flush(self): try: while self.running: with self.lockTopicsToPublish: records = list(self.topicsToPublish.values()) self.topicsToPublish.clear() if len(records) > 0: topicDataToSend = TopicData() topicDataList = TopicDataRecordList() for rec in records: topicDataList.elements.append(rec) topicDataToSend.topic_data_record_list.CopyFrom(topicDataList) msg = topicDataToSend.SerializeToString() self.ws.send(msg) time.sleep(self.publishFrequency) except websockets.exceptions.WebSocketException as e: print('Error while publishing topicData to the masternode') print(e) self.node.events.onConnectionError(e) except Exception as e: self.node.events.onPublishError(e) print(e) def __writeSocket(self): self.__flush() def __readSocket(self): self.__recvMessage() def __recvMessage(self): try: with connect(self.endpoint) as socket: while self.running: message = None while True and self.running: try: message = socket.recv(2) break except TimeoutError: if self.running: print('timeout while recieving, trying again') if message == "PING": socket.send('PONG') elif not message is None: topicData = TopicData() topicData.ParseFromString(message) if topicData.HasField('topic_data_record'): self.__invokeCallbacks(topicData.topic_data_record) if topicData.HasField('topic_data_record_list'): for record in topicData.topic_data_record_list.elements: self.__invokeCallbacks(record) if topicData.HasField('error'): print('topicData receive error:') print(topicData.error) except websockets.exceptions.WebSocketException as e: print('Error while receiving form the masternode') print(e) self.node.events.onConnectionError(e) except Exception as e: self.node.events.onReadError(e) print(e) def __sendData(self, data: str): try: self.ws.send(data) except websockets.exceptions.WebSocketException as e: print('Error sending topicdata to masternode') print(e) self.node.events.onConnectionError(e)
[docs] async def sendTopicData(self, record: TopicData): msg = record.SerializeToString() self.__sendData(msg)
[docs] def isSubscribed(self, topicRegex: str) -> bool: with self.lockTopics: isSub = topicRegex in self.topicCallbacks with self.lockRegex: return isSub or topicRegex in self.regexCallbacks
[docs] def addTopicCallback(self, topic: str, callback: Callable[[TopicDataRecord], None]): with self.lockTopics: if not topic in self.topicCallbacks: self.topicCallbacks[topic] = [] self.topicCallbacks[topic].append(callback)
[docs] def addRegexCallback(self, regex: str, callback: Callable[[TopicDataRecord], None]): with self.lockRegex: if not regex in self.regexCallbacks: self.regexCallbacks[regex] = [] self.regexCallbacks[regex].append(callback)
[docs] def removeTopicCallback(self, topic: str, callback: Callable[[TopicDataRecord], None]): with self.lockTopics: if topic in self.topicCallbacks: listToRemoveFrom = self.topicCallbacks[topic] if not listToRemoveFrom is None: if callback in listToRemoveFrom: listToRemoveFrom.remove(callback)
[docs] def removeRegexCallback(self, regex: str, callback: Callable[[TopicDataRecord], None]): with self.lockRegex: if regex in self.regexCallbacks: listToRemove = self.regexCallbacks[regex] if not listToRemove is None: if callback in listToRemove: listToRemove.remove(callback)
[docs] def hasTopicDataCallbacks(self, topic: str) -> bool: if not self.isSubscribed(topic): return False with self.lockTopics: subs = self.topicCallbacks[topic] if (subs is None) or (not subs): return False return True
[docs] def hasRegexCallbacks(self, regex: str): if not self.isSubscribed(regex): return False with self.lockRegex: subs = self.regexCallbacks[regex] if (subs is None) or (not subs): return False return True
[docs] def stopNode(self): self.running = False self.ws.close()
[docs] def removeTopicDataCallbacks(self, topic: str): with self.lockTopics: del self.topicCallbacks[topic]
[docs] def removeAllRegexCallbacks(self, regex: str): with self.lockRegex: del self.regexCallbacks[regex]
def __invokeCallbacks(self, record: TopicDataRecord): topic = record.topic with self.lockTopics: inTopics = topic in self.topicCallbacks if inTopics: for callback in self.topicCallbacks[topic]: callback(record) with self.lockRegex: if not inTopics: for key, value in self.regexCallbacks.items(): if re.match(key, topic): for callback in value: callback(record)
[docs] def setPublishFrequency(self, frequency): self.publishFrequency = frequency