Newer
Older
from threading import Thread
import time
class CommClient(Thread):
"""
The ProtocolHandler class is the generic interface class between the tango attribute_wrapper and the outside world
"""
def __init__(self, fault_func, streams, try_interval=2):
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"""
"""
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.stopping = False
while not self.stopping:
# keep trying to connect
if not self.connected:
if self.connect():
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):
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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.
It should do this by first calling: _setup_annotation and setup_value_conversion to get all data necceacry to configure the read/write functions.
It should then return the read and write functions to the attribute.
MANDATORY:
annotation_outputs = _setup_annotation(annotation)
attribute_outputs = _setup_annotation(attribute)
(note: outputs are up to the user)
REQUIRED: provide read and write functions to return, there are no restrictions on how these should be provided,
except that the read function takes a single input value and the write function returns a single value
MANDATORY:
return read_function, write_function
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.
"""
raise NotImplementedError("the setup_attribute must be implemented and provide return a valid read/write function for the attribute")
def _setup_annotation(self, annotation):
"""
This function is responsible for handling the annotation data provided by the attribute to configure the read/write function the client must provide.
This function should be called by setup_attribute
"""
raise NotImplementedError("the _setup_annotation must be implemented, content and outputs are up to the user")
def setup_value_conversion(self, attribute):
"""
this function is responsible for setting up the value conversion between the client and the attribute.
This function should be called by setup_attribute
"""
raise NotImplementedError("the setup_value_conversion must be implemented, content and outputs are up to the user")