Skip to content
Snippets Groups Projects
comms_client.py 3.06 KiB
Newer Older
from threading import Thread
import socket
import time
import numpy

import opcua
from opcua import Client

from tango import DevState


class CommClient(Thread):
	"""
	The ProtocolHandler class is the generic interface class between the tango attribute_wrapper and the outside world
	"""

	def __init__(self, standby_func, fault_func, streams, try_interval=2):
		"""

		"""
		self.standby_func = standby_func
		self.fault_func = fault_func
		self.try_interval = try_interval
		self.streams = streams
		self.stopping = False
		self.connected = False

		super().__init__(daemon=True)

	def connect(self):
		"""
		Function used to connect to the client.
		"""
		self.connected = True
		return True

	def disconnect(self):
		"""
		Function used to connect to the client.
		"""
		self.connected = False

	def run(self):

		# Explicitly connect
		if not self.connect():
			# hardware or infra is down -- needs fixing first
			self.fault_func()
			return

		self.standby_func()

		self.stopping = False
		while not self.stopping:
			# keep trying to connect
			print("connected check")
			if not self.connected:
				print("not connected, try to connect")
				if self.connect():
					print("connected now, call on_func")
					self.standby_func()
				else:
					# we retry only once, to catch exotic network issues. if the infra or hardware is down,
					# our device cannot help, and must be reinitialised after the infra or hardware is fixed.
					self.fault_func()
					return

			# keep checking if the connection is still alive
			try:
				while not self.stopping:
					self.ping()
					time.sleep(self.try_interval)
			except Exception as e:
				self.streams.error_stream("Fault condition in communication detected.", e)

				# technically, we may not have dropped the connection, but encounter a different error. so explicitly disconnect.
				self.disconnect()

				# signal that we're disconnected
				self.fault_func()

	def ping(self):
		pass

	def stop(self):
		"""
		  Stop connecting & disconnect. Can take a few seconds for the timeouts to hit.
		"""

		if not self.ident:
			# have not yet been started, so nothing to do
			return

		self.stopping = True
		self.join()

		self.disconnect()

	def setup_attribute(self, annotation, attribute):
		This function is responsible for providing the attribute_wrapper with a read/write function
		How this is done is implementation specific.
		The setup-attribute has access to the comms_annotation provided to the attribute wrapper to pass along to the comms client
		as well as a reference to the attribute itself.

		Examples:
		- File system:  get_mapping returns functions that read/write a fixed
		number of bytes at a fixed location in a file. (SEEK)
		- OPC-UA:  traverse the OPC-UA tree until the node is found.
		Then return the read/write functions for that node which automatically
		convert values between Python and OPC-UA.
		AssertionError("the setup_attribute must be implemented and provide return a valid read/write function for the attribute")