-
Notifications
You must be signed in to change notification settings - Fork 14
/
block.py
161 lines (130 loc) · 5.35 KB
/
block.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
from collections import OrderedDict
from malcolm.core.notifier import Notifier
from malcolm.core.serializable import Serializable
from malcolm.core.request import Request
from malcolm.core.response import Response
from malcolm.core.attribute import Attribute
from malcolm.core.method import Method
class DummyLock(object):
def acquire(self):
pass
def release(self):
pass
def __enter__(self):
self.acquire()
def __exit__(self, type_, value, traceback):
self.release()
return False
class LockRelease(object):
def __init__(self, lock):
self.lock = lock
def __enter__(self):
self.lock.release()
def __exit__(self, type_, value, traceback):
self.lock.acquire()
return False
@Serializable.register_subclass("malcolm:core/Block:1.0")
class Block(Notifier):
"""Object consisting of a number of Attributes and Methods"""
def __init__(self, name):
"""
Args:
name (str): Block name e.g. "BL18I:ZEBRA1"
"""
super(Block, self).__init__(name=name)
self.name = name
self.methods = OrderedDict()
self.attributes = OrderedDict()
self.lock = DummyLock()
def add_attribute(self, attribute, notify=True):
"""Add an Attribute to the block and set the block as its parent"""
self.add_child(attribute, self.attributes)
self.on_changed([[attribute.name], attribute.to_dict()], notify)
def add_method(self, method, notify=True):
"""Add a Method to the Block
Args:
method (Method): The Method object that has already been filled in
"""
self.add_child(method, self.methods)
self.on_changed([[method.name], method.to_dict()], notify)
def add_child(self, attribute_or_method, d):
"""Add an Attribute or Method to the block and set the block as its
parent, but don't notify"""
child_name = attribute_or_method.name
assert not hasattr(self, child_name), \
"Attribute or Method %s already defined for Block %s" \
% (child_name, self.name)
setattr(self, child_name, attribute_or_method)
d[child_name] = attribute_or_method
attribute_or_method.set_parent(self)
def _where_child_stored(self, child):
if isinstance(child, Method):
return self.methods
elif isinstance(child, Attribute):
return self.attributes
def update(self, change):
"""Update block given a single change.
Delegates to children update methods if possible.
Args:
change [[path], new value]: Path to changed element and new value
"""
name = change[0][0]
if hasattr(self, name):
# sub-structure exists in block - delegate down
# TODO: handle removal?
getattr(self, name).update([change[0][1:], change[1]])
else:
# sub-structure does not exist - create and add
if len(change[0]) > 1:
raise ValueError("Missing substructure at %s" % name)
child = Serializable.deserialize(name, change[1])
d = self._where_child_stored(child)
assert d is not None, \
"Change %s deserialized to unknown object %s" % (change, child)
self.add_child(child, d)
def replace_children(self, children, notify=True):
for method_name in self.methods:
delattr(self, method_name)
self.methods.clear()
for attr_name in self.attributes:
delattr(self, attr_name)
self.attributes.clear()
for child in children:
d = self._where_child_stored(child)
assert d is not None, \
"Don't know how to add a child %s" % child
self.add_child(child, d)
self.on_changed([[], self.to_dict()], notify)
def notify_subscribers(self):
if self.parent is not None:
self.parent.notify_subscribers(self.name)
def handle_request(self, request):
"""
Process the request depending on the type
Args:
request(Request): Request object specifying action
"""
self.log_debug("Received request %s", request)
assert request.type_ == Request.POST or request.type_ == Request.PUT, \
"Expected Post or Put request, received %s" % request.type_
with self.lock:
if request.type_ == Request.POST:
method_name = request.endpoint[-1]
response = self.methods[method_name].get_response(request)
elif request.type_ == Request.PUT:
attr_name = request.endpoint[-1]
self.attributes[attr_name].put(request.value)
self.attributes[attr_name].set_value(request.value)
response = Response.Return(request.id_, request.context)
self.parent.block_respond(response, request.response_queue)
def to_dict(self):
"""Convert object attributes into a dictionary"""
d = OrderedDict()
d["typeid"] = self.typeid
for attribute_name, attribute in self.attributes.items():
d[attribute_name] = attribute.to_dict()
for method_name, method in self.methods.items():
d[method_name] = method.to_dict()
return d
def lock_released(self):
return LockRelease(self.lock)