Source code for stdcomqt5

from PyQt5.QtCore import QTimer, QObject, pyqtSignal, pyqtSlot, QSettings
from stdcom import *

"""
    This is where Stec QObject are kept, this are not QWidgets
"""
[docs]class VSettings(QSettings): """ Used to save setup data """ def __init__(self, project : str = "stec-opcua"): """ :param project: default is "stec-opc" Should be the Project you or instance of the Project """ super().__init__( project, QSettings.IniFormat)
[docs]class stdcomPyQt(QObject): """ The stdcomPyQt class is a PyQt5 version that sits on top of stdcom, can has signals, slots, and auto restart capabilities build into it. :uiparent: QObject = None default, or the parent QObject """ sigNewData = pyqtSignal(str, list) sigNames = pyqtSignal(list) sigConnect = pyqtSignal() sigNoConnect = pyqtSignal() sigBalanceTable = pyqtSignal() cBridge = None Parent = None liveSubnames = [] watchDogSeconds = 5 # this is a duplicate, but it allows us to stay running if the Links to Multiverse is down.. liveData = {} timer = None MultiverseHostname = "192.168.199.7" MultiversePort = 4897 def __init__(self, uiparent: QObject = None): """ :param uiparent: QObject of the parent or None """ if uiparent is not None: super().__init__(uiparent) else: super().__init__() self.Parent = uiparent self.timer = QTimer(self) self.timer.timeout.connect(self.RestartAttmepts) self.timer.setInterval(self.watchDogSeconds * 1000)
[docs] def setWatchDogIntervalSec(self, seconds: int = 5): """ Sets the watchdog timer in seconds. This can be adjusted if the Multiverse in on the internet and thousands of miles away. :seconds: Default is 5 seconds, make this longer if huge internet delay """ self.watchDogSeconds = seconds self.timer.setInterval(self.watchDogSeconds * 1000)
[docs] def readValues(self, Name: str): """ Reads values cached for a single subscription name that is given and has been subscribed to. :Name: Name of the subscription :return: list of data for a given subscrition, or None if not available """ if Name in self.liveData.keys(): return self.liveData.get(Name) return None
[docs] def writeValues(self, Name: str, Data: list): """ WritesValues to multiverse if subcription is made, or local data is ~/ is the prefix :Name: The name of the subscription, if ~/ is the prefix, then data is local. :Data: Is a list[] of data """ if Name in self.liveData.keys(): self.liveData.update({Name: Data}) if self.cBridge is not None and Data is not None: Data = list(Data) if len(Data): self.cBridge.UpdateRaw(Name, Data, 0) # allow the system to operate without Multiverse else: self.sigNewData.emit(Name, Data)
[docs] def subscribe(self, Name: str): """ Subscribe to a give Name :Name: The subscription name """ if Name not in self.liveData.keys(): self.liveData.update({Name: []}) if self.cBridge is not None: self.cBridge.AddSubscriptions([Name])
[docs] def getPossibleSubscribesr(self): """ All the possible subscription names possible :return: list of names of all possible subscritions """ return self.liveSubnames
[docs] def getSubscribers(self): """ All the stuff subscribed to :return: A list of all the subscription names we have subscribed to """ return list(self.liveData.keys())
[docs] def unsubscribe(self, Name): """ UnSubscribe to something previously subscribed to. :Name: The name of the Subscription to delete """ if Name in self.liveData.keys(): del self.liveData[Name] if self.cBridge is not None: self.cBridge.RemoveSub(Name)
[docs] def AddValues(self, sub): """ Internal use, updates makes and notfies by signal data has changed for a subcription :sigNewData.emit: Name, Data """ self.liveData.update({sub.Name(): sub.Data()}) self.sigNewData.emit(sub.Name(), sub.Data())
[docs] def AddNames(self, values): """ Internal use tell when a new name is found, with emit : sigNames.emit: list[str] of new names entering the picture that can be subscribed to """ print("AddNames") self.sigNames.emit(values)
[docs] def ConnectSocket(self, v): """ Socket connection call back, for internal use :sigConnect.emit: emits the signal if connection happens """ self.sigConnect.emit() print(v)
[docs] def ConnectionError(self): """ Socket error connection call back, for internal use :sigNoConnect.emit: emits the signal if connection error or close happens """ self.sigNoConnect.emit() print("Connection Error")
[docs] def setDestination(self, dest, port): """ Sets a new Destination and port, the user must terminate amd loadbridge again after changing is connected. """ self.MultiverseHostname = dest self.MultiversePort = port
[docs] def LoadcBridge(self): """ User calls this to start the ball rolling to the connection to multiverse providing the destination and port are set correctly. """ if self.cBridge is not None: self.liveSubnames = [] self.cBridge.terminate() self.sigNames.disconnect(self.NewLiveTags) try: self.cBridge = stdcom(self.MultiverseHostname, self.MultiversePort, self.ConnectSocket, None, None, self.ConnectionError) self.cBridge.SetCallbacks(self.AddValues, self.AddNames) self.cBridge.NamesOn() # tell the communication class to tu self.sigNames.connect(self.NewLiveTags) self.sigConnect.emit() subs = list(self.liveData.keys()) if subs is not None and len(subs) > 0: self.cBridge.AddSubscriptions(subs) else: print("No Subscriptions to Add") self.timer.start() except ConnectionError as e: print("Need To Set Correct IP and Port") self.cBridge = None self.sigNoConnect.emit()
# ----- from threads
[docs] def terminate(self): """ Cridical the user called this function when leaving .. I closes down the thread connected to multiverse """ if self.cBridge is not None: self.timer.stop() self.cBridge.terminate() self.cBridge = None self.sigNoConnect.emit()
@pyqtSlot(str, list) def slotUpdateData(self, Name, Data): self.writeValues(Name, Data) @pyqtSlot(list) def NewLiveTags(self, names): self.liveSubnames = self.liveSubnames + names @pyqtSlot() def RestartAttmepts(self): # todo needs better logic, this works but logic is weak and needs something besides order and 1 default if self.cBridge is None or self.cBridge.isConnected() is False: self.LoadcBridge() elif self.cBridge != None: acks = self.cBridge.GetAcks() print("ACKS: ", acks) if acks == 0: self.LoadcBridge() else: self.cBridge.Ping()
if __name__ == "__main__": from PyQt5.QtCore import QCoreApplication import sys if "--version" in sys.argv: print("1.2.3") sys.exit() app = QCoreApplication(sys.argv) w = stdcomPyQt() w.LoadcBridge() app.exec_() w.terminate() sys.exit(0)