Skip to content

Commit

Permalink
ongoing - fixed up lots of tests, refactored Metod
Browse files Browse the repository at this point in the history
  • Loading branch information
gilesknap committed Jul 29, 2016
1 parent e5a1903 commit de3abd9
Show file tree
Hide file tree
Showing 26 changed files with 394 additions and 378 deletions.
12 changes: 7 additions & 5 deletions malcolm/controllers/clientcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ class ClientController(Controller):
REMOTE_BLOCKS_ID = 0
BLOCK_ID = 1

def __init__(self, process, block):
def __init__(self, process, block, block_name):
"""
Args:
process (Process): The process this should run under
block (Block): The local block we should be controlling
block_name (str): The local block's name
"""
super(ClientController, self).__init__(block=block, process=process)
super(ClientController, self).__init__(block=block, process=process,
block_name=block_name)
request = Subscribe(
None, self, [self.process.name, "remoteBlocks", "value"])
request.set_id(self.REMOTE_BLOCKS_ID)
Expand All @@ -42,12 +44,12 @@ def put(self, response):
self.block.update(change)

def _regenerate_block(self, d):
children = []
children = {}
for k, v in d.items():
if k == "typeid":
continue
child = Serializable.deserialize(k, v)
children.append(child)
child = Serializable.from_dict(v)
children[k] = child
if isinstance(child, Method):
# calling method forwards to server
child.set_function(
Expand Down
6 changes: 3 additions & 3 deletions malcolm/controllers/countercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
class CounterController(Controller):

def create_attributes(self):
self.counter = Attribute(
"counter", NumberMeta("meta", "A counter", np.int32))
self.counter = Attribute(NumberMeta(description="A counter"))
self.counter.meta.set_dtype('uint32')
self.counter.set_put_function(self.counter.set_value)
self.counter.set_value(0)
yield self.counter
yield "counter", self.counter

@Controller.Resetting
def do_reset(self):
Expand Down
4 changes: 2 additions & 2 deletions malcolm/controllers/hellocontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@


class HelloController(Controller):
@takes(StringMeta("name", "a name"), REQUIRED)
@returns(StringMeta("greeting", "a greeting"), REQUIRED)
@takes("name", StringMeta(description="a name"), REQUIRED)
@returns("greeting", StringMeta(description="a greeting"), REQUIRED)
def say_hello(self, parameters, return_map):
"""Says Hello to name
Expand Down
33 changes: 17 additions & 16 deletions malcolm/controllers/scanpointtickercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@
class ScanPointTickerController(Controller):

def create_attributes(self):
self.value = Attribute("value", NumberMeta(
"meta", "Value", numpy.float64))
yield self.value
self.value = Attribute(NumberMeta(description="Value"))
self.value.meta.set_dtype('float64')
yield 'value', self.value
self.generator = Attribute(
"generator", PointGeneratorMeta("meta", "Scan Point Generator"))
yield self.generator
self.axis_name = Attribute(
"axis_name", StringMeta("meta", "Name of the axis"))
yield self.axis_name
self.exposure = Attribute(
"exposure", NumberMeta("meta", "Exposure time", numpy.float64))
yield self.exposure

@takes(PointGeneratorMeta("generator", "Generator instance"), REQUIRED,
StringMeta("axis_name", "Specifier for axis"), REQUIRED,
NumberMeta("exposure",
"Detector exposure time", numpy.float64), REQUIRED)
PointGeneratorMeta(description="Scan Point Generator"))
yield "generator", self.generator
self.axis_name = Attribute(StringMeta(description="Name of the axis"))
yield "axis_name", self.axis_name
self.exposure = Attribute(NumberMeta(description="Exposure time"))
self.value.meta.set_dtype('float64')
yield "exposure", self.exposure

@takes("generator", PointGeneratorMeta(
description="Generator instance"), REQUIRED,
"axis_name", StringMeta( description="Specifier for axis"), REQUIRED,
"exposure", NumberMeta(
description="Detector exposure time"), REQUIRED)
def configure(self, params):
"""
Configure the controller
Expand All @@ -42,6 +42,7 @@ def configure(self, params):
self.generator.set_value(params.generator)
self.axis_name.set_value(params.axis_name)
self.exposure.set_value(params.exposure)
self.exposure.meta.set_dtype('float64')
self.block.notify_subscribers()

@Method.wrap_method
Expand Down
2 changes: 1 addition & 1 deletion malcolm/core/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, meta=None):

def set_meta(self, meta, notify=True):
"""Set the ScalarMeta object"""
if isinstance(meta, (dict, OrderedDict)):
if isinstance(meta, dict):
meta = Serializable.from_dict(meta)
assert isinstance(meta, ScalarMeta), \
"Expected meta to be a ScalarMeta subclass, got %s" % (meta,)
Expand Down
33 changes: 9 additions & 24 deletions malcolm/core/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ def __exit__(self, type_, value, traceback):
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)
def __init__(self):
self.methods = OrderedDict()
self.attributes = OrderedDict()
self.lock = DummyLock()
Expand All @@ -54,30 +49,29 @@ def __init__(self, name):
def endpoints(self):
return list(self.attributes.keys()) + list(self.methods.keys())

def add_attribute(self, attribute, notify=True):
def add_attribute(self, child_name, attribute, notify=True):
"""Add an Attribute to the block and set the block as its parent"""
self.add_child(attribute, self.attributes)
self.add_child(child_name, attribute, self.attributes)
self.on_changed([[attribute.name], attribute.to_dict()], notify)

def add_method(self, method, notify=True):
def add_method(self, child_name, 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.add_child(child_name, method, self.methods)
self.on_changed([[method.name], method.to_dict()], notify)

def add_child(self, attribute_or_method, d):
def add_child(self, child_name, 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)
attribute_or_method.set_parent(self, child_name)

def _where_child_stored(self, child):
if isinstance(child, Method):
Expand Down Expand Up @@ -114,11 +108,11 @@ def replace_children(self, children, notify=True):
for attr_name in self.attributes:
delattr(self, attr_name)
self.attributes.clear()
for child in children:
for name, child in children.items():
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.add_child(name, child, d)
self.on_changed([[], self.to_dict()], notify)

def notify_subscribers(self):
Expand Down Expand Up @@ -152,15 +146,6 @@ def handle_request(self, request):
response = Return(request.id_, request.context)
self.parent.block_respond(response, request.response_queue)

def to_dict(self):
"""Convert object attributes into a dictionary"""

overrides = {}
for attribute_name, attribute in self.attributes.items():
overrides[attribute_name] = attribute.to_dict()
for method_name, method in self.methods.items():
overrides[method_name] = method.to_dict()
return super(Block, self).to_dict(**overrides)

def lock_released(self):
return LockRelease(self.lock)
40 changes: 19 additions & 21 deletions malcolm/core/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,27 @@ class Controller(Loggable):

Resetting = Hook()

def __init__(self, process, block):
def __init__(self, process, block, block_name):
"""
Args:
process (Process): The process this should run under
block (Block): Block instance to add Methods and Attributes to
"""
self.set_logger_name("%s.controller" % block.name)
block.set_parent(process, block_name)
self.set_logger_name("%s.controller" % block_name)

# dictionary of dictionaries
# {state (str): {Method: writeable (bool)}
self.methods_writeable = {}
self.process = process
self.parts = []
self.block = block
for attribute in self._create_default_attributes():
block.add_attribute(attribute)
for attribute in self.create_attributes():
block.add_attribute(attribute)
for method in self.create_methods():
block.add_method(method)
for name, attribute in self._create_default_attributes():
block.add_attribute(name, attribute)
for name, attribute in self.create_attributes():
block.add_attribute(name, attribute)
for name, method in self.create_methods():
block.add_method(name, method)
# Set if the method is writeable
if method.only_in is None:
states = [state for state in self.stateMachine.possible_states
Expand All @@ -49,8 +50,6 @@ def __init__(self, process, block):
(state, self.stateMachine.possible_states)
self.set_method_writeable_in(method, states)

self.process.add_block(block)

def create_methods(self):
"""Abstract method that should provide Method instances for Block
Expand All @@ -65,7 +64,7 @@ def create_methods(self):
for member in members:
if hasattr(member, "Method"):
member.Method.set_function(member)
yield member.Method
yield (member.__name__, member.Method)

def create_attributes(self):
"""Abstract method that should provide Attribute instances for Block
Expand All @@ -77,19 +76,18 @@ def create_attributes(self):
return iter(())

def _create_default_attributes(self):
self.state = Attribute(
"state", ChoiceMeta("meta", "State of Block",
self.stateMachine.possible_states))
self.state = Attribute(ChoiceMeta(description="State of Block",
choices=self.stateMachine.possible_states))
self.state.set_parent(self.block,'state')
self.state.set_value(self.stateMachine.DISABLED)
yield self.state
self.status = Attribute(
"status", StringMeta("meta", "Status of Block"))
yield ('state', self.state)
self.status = Attribute(StringMeta(description="Status of Block"))
self.status.set_value("Disabled")
yield self.status
self.busy = Attribute(
"busy", BooleanMeta("meta", "Whether Block busy or not"))
yield ('status', self.status)
self.busy = Attribute(BooleanMeta(
description="Whether Block busy or not"))
self.busy.set_value(False)
yield self.busy
yield ('busy', self.busy)

@takes()
@only_in(sm.DISABLED, sm.FAULT)
Expand Down
31 changes: 17 additions & 14 deletions malcolm/core/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, meta, d=None):
d = {} if d is None else d
for key, value in d.items():
if key in meta.elements:
setattr(self, key, value)
self[key] = value
else:
raise ValueError("%s is not a valid key for given meta" % key)

Expand All @@ -33,23 +33,26 @@ def __eq__(self, rhs):
def __ne__(self, rhs):
return not self.__eq__(rhs)

def __setitem__(self, key, val):
setattr(self, key, val)

def __setattr__(self, attr, val):
if hasattr(self, "meta"):
if attr not in self.meta.elements:
raise ValueError("%s is not a valid key for given meta" % attr)
val = self.meta.elements[attr].validate(val)
object.__setattr__(self, attr, val)
self[attr] = val
else:
object.__setattr__(self, attr, val)

def __setitem__(self, key, val):
if key not in self.meta.elements:
raise ValueError("%s is not a valid key for given meta" % key)
val = self.meta.elements[key].validate(val)
object.__setattr__(self, key, val)

def __getitem__(self, key):
if key == "meta" or not hasattr(self, key):
if key in self.endpoints:
return getattr(self, key)
else:
raise KeyError
return getattr(self, key)

def __contains__(self, key):
return key != "meta" and hasattr(self, key)
return key in self.endpoints

def __len__(self):
return len(self.endpoints)
Expand All @@ -62,7 +65,7 @@ def update(self, d):
if not set(d).issubset(self.meta.elements):
raise ValueError("%s contains invalid keys for given meta" % d)
for k in d:
setattr(self, k, d[k])
self[k] = d[k]

def clear(self):
for e in self.meta.elements:
Expand All @@ -73,10 +76,10 @@ def keys(self):
return self.endpoints

def values(self):
return [getattr(self, e) for e in self.endpoints]
return [self[e] for e in self.endpoints]

def items(self):
return [(e, getattr(self, e)) for e in self.endpoints]
return [(e, self[e]) for e in self.endpoints]

@classmethod
def from_dict(cls, d, meta):
Expand Down

0 comments on commit de3abd9

Please sign in to comment.