-
Notifications
You must be signed in to change notification settings - Fork 13
/
io_driver.py
184 lines (154 loc) · 5.97 KB
/
io_driver.py
1
2
3
4
5
6
7
8
9
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import threading as t
from enthought.traits.api import HasTraits, Int, Str, List, DelegatesTo, Instance
from data_decoder import DataDecoder
from variables import Variables
from viewers import Viewers
class IODriver(t.Thread, HasTraits):
"""
Base class for a generic input driver. Runs in its own thread and grabs input from the
source and passes it out to the decoding layer.
"""
_variables = Instance(Variables)
_decoders = List(DataDecoder)
_wants_to_terminate = False
_use_thread = True
name = Str('Input Driver')
def __init__(self, **kwargs):
t.Thread.__init__(self)
HasTraits.__init__(self, **kwargs)
def open(self):
"""
Here is a place to put any initialisation needed to start your driver, e.g. opening
a port or file.
"""
pass
def close(self):
"""
Here is a place to put any code needed to cleanly stop your input driver e.g. closing a port
"""
pass
def receive(self):
"""
In this function you should add the code to setup and read from
your input source. Return the received data to be passed to the data
decoding layer. This function is called repeatedly while the driver
is running and should not block indefinately (a timeout should be
used to allow the driver to stop). You can return None is no data is available.
"""
return None
def run(self):
""" Used internally for the threading interface, you should put your code in receive. """
if self._use_thread:
while not self._wants_to_terminate:
data = self.receive()
if data:
self.pass_data(data)
print "IO driver thread terminating:", self.name
try:
self.close()
except Exception as e:
print "Exception closing IO driver '%s':" % self.name, e
def start(self):
""" Used internally to start the thread for the input driver, if you have init code put it in open. """
try:
self.open()
except Exception as e:
print "Exception opening IO driver '%s':" % self.name, e
self.stop()
t.Thread.start(self)
def stop(self):
""" Used internally to stop the thread for the input driver, if you have clean-up code put it in close. """
if self._use_thread:
self._wants_to_terminate = True
else:
try:
self.close()
except Exception as e:
print "Exception closing IO driver '%s':" % self.name, e
def _add_decoder(self, decoder):
""" Used internally to add decoders so they receive data from the input driver. """
decoder._variables = self._variables
self._decoders += [decoder]
def _remove_decoder(self, decoder):
""" Used internally to remove decoders from an input driver. """
self._decoders.remove(decoder)
def _remove_all_decoders(self):
for decoder in self._decoders:
self._remove_decoder(decoder)
def pass_data(self, data):
""" Pass data on to the decoding layer. """
for decoder in self._decoders: #self._decoder_list.get_decoders():
decoder._receive_callback(data)
def get_config(self):
print "Warning: using defualt get_config handler on io driver '%s' (which may not work)." % self.__class__.__name__
state = self.__getstate__()
for key in list(state.iterkeys()):
if key.startswith('_'):
del state[key]
print " config:", state
return state
def set_config(self, config):
print "Warning: using defualt set_config handler on io driver '%s' (which may not work)." % self.__class__.__name__
print " config:", config
self.__setstate__(config)
def _get_config(self):
# get decoders
decoder_configs = []
for decoder in self._decoders:
decoder_config = {decoder.__class__.__name__: decoder.get_config()}
decoder_configs.append(decoder_config)
config = self.get_config()
config.update({'decoders': decoder_configs})
return config
def _set_config(self, config):
from plugin_manager import get_decoder_plugin_by_name
# add decoders
self._remove_all_decoders()
for decoder_config in config['decoders']:
decoder_plugin_name = list(decoder_config.iterkeys())[0]
decoder_plugin_config = decoder_config[decoder_plugin_name]
new_decoder = get_decoder_plugin_by_name(decoder_plugin_name)()
self._add_decoder(new_decoder)
new_decoder.set_config(decoder_plugin_config)
del config['decoders']
self.set_config(config)
class IODriverList(HasTraits):
"""
Maintains the list of input drivers currently in use and provides
facilities to add and remove drivers.
"""
io_drivers = List(IODriver)
viewers = DelegatesTo('viewers_instance')
viewers_instance = Instance(Viewers)
variables = Instance(Variables)
def start_all(self):
map(lambda d: d.start(), self.io_drivers)
def stop_all(self):
map(lambda d: d.stop(), self.io_drivers)
def _remove_io_driver(self, io_driver):
print "Removing IO driver:", io_driver.name
io_driver.stop()
self.io_drivers.remove(io_driver)
def _remove_all_io_drivers(self):
for io_driver in self.io_drivers:
self._remove_io_driver(io_driver)
def _add_io_driver(self, io_driver):
print "Adding IO driver:", io_driver.name
io_driver._variables = self.variables
io_driver.start()
self.io_drivers.append(io_driver)
def get_config(self):
config = []
for io_driver in self.io_drivers:
io_driver_config = {io_driver.__class__.__name__: io_driver._get_config()}
config.append(io_driver_config)
return config
def set_config(self, config):
from plugin_manager import get_io_driver_plugin_by_name
self._remove_all_io_drivers()
for io_driver_config in config:
io_driver_plugin_name = list(io_driver_config.iterkeys())[0]
io_driver_plugin_config = io_driver_config[io_driver_plugin_name]
new_io_driver = get_io_driver_plugin_by_name(io_driver_plugin_name)()
self._add_io_driver(new_io_driver)
new_io_driver._set_config(io_driver_plugin_config)