def set_table_cls(self, table_cls=None): # type: (Type[Table]) -> None if table_cls is None or table_cls.__name__ == "TableSubclass": # Either autogenerated by this function or not set, so make one class TableSubclass(Table): def __init__(self, **kwargs): # type: (**Any) -> None self.__dict__.update(kwargs) table_cls = TableSubclass for k, meta in self.elements.items(): # We can distinguish the type by asking for the default # validate value default_array = meta.validate(None) # type: Array anno = Anno(meta.description, default_array.typ, k) anno.is_array = True anno.is_mapping = False table_cls.call_types[k] = anno else: # User supplied, check it matches element names assert issubclass(table_cls, Table), \ "Expecting table subclass, got %s" % (table_cls,) missing = set(self.elements) - set(table_cls.call_types) assert not missing, "Supplied Table missing fields %s" % ( missing, ) extra = set(table_cls.call_types) - set(self.elements) assert not extra, "Supplied Table has extra fields %s" % (extra, ) self.table_cls = table_cls
def set_table_cls(self, table_cls: Type[Table] = None) -> None: if table_cls is None or table_cls.__name__ == "TableSubclass": # Either autogenerated by this function or not set, so make one class TableSubclass(Table): def __init__(self, **kwargs: Any) -> None: self.__dict__.update(kwargs) table_cls = TableSubclass for k, meta in self.elements.items(): # We can distinguish the type by asking for the default # validate value default_array: Array = meta.validate(None) anno = Anno(meta.description, name=k).set_typ( default_array.typ, is_array=True ) table_cls.call_types[k] = anno else: # User supplied, check it matches element names assert Table.matches_type( table_cls ), f"Expecting table subclass, got {table_cls}" missing = set(self.elements) - set(table_cls.call_types) assert not missing, f"Supplied Table missing fields {missing}" extra = set(table_cls.call_types) - set(self.elements) assert not extra, f"Supplied Table has extra fields {extra}" self.table_cls = table_cls
def __init__(self, generator, axesToMove=None, **kwargs): # type: (AGenerator, UAxesToMove, **Any) -> None if kwargs: # Got some additional args to report self.call_types = self.call_types.copy() for k in kwargs: # We don't use this apart from its presence, # so no need to fill in description, typ, etc. self.call_types[k] = Anno("") self.__dict__.update(kwargs) self.generator = generator if axesToMove is None: axesToMove = generator.axes self.axesToMove = AAxesToMove(axesToMove)
def set_endpoint_data(self, name, value): # type: (str, ModelOrDict) -> Any name = deserialize_object(name, str_) if name == "meta": value = deserialize_object(value, BlockMeta) else: value = deserialize_object(value, (AttributeModel, MethodModel)) with self.notifier.changes_squashed: if name in self.call_types: # Stop the old Model notifying getattr(self, name).set_notifier_path(Model.notifier, []) else: anno = Anno("Field", typ=type(value)) self.call_types[name] = anno value.set_notifier_path(self.notifier, self.path + [name]) setattr(self, name, value) # Tell the notifier what changed self.notifier.add_squashed_change(self.path + [name], value) self._update_fields() return value
def creator_with_nice_signature(creator, sections, yamlname, yaml_path, docstring): takes = _create_takes_arguments(sections) args = [] for anno in takes: if anno.default is NO_DEFAULT: args.append(anno.name) else: args.append(f"{anno.name}={anno.default!r}") func = f""" def creator_from_yaml({', '.join(args)}): return creator(locals())""" # Copied from decorator pypi module code = compile(func, yaml_path, "single") exec(code, locals()) ret = locals()["creator_from_yaml"] ret.return_type = Anno("Any return value", Any, "return") ret.call_types = OrderedDict((anno.name, anno) for anno in takes) ret.__doc__ = docstring ret.__name__ = yamlname ret.yamlname = yamlname return ret
def __init__(self, generator: AGenerator, axesToMove: UAxesToMove = None, breakpoints: ABreakpoints = None, **kwargs: Any) -> None: if kwargs: # Got some additional args to report self.call_types: Dict[str, Anno] = ConfigureParams.call_types.copy() for k in kwargs: # We don't use this apart from its presence, # so no need to fill in description, typ, etc. self.call_types[k] = Anno("") self.__dict__.update(kwargs) self.generator = generator if axesToMove is None: axesToMove = generator.axes self.axesToMove = AAxesToMove(axesToMove) if breakpoints is None: breakpoints = [] self.breakpoints = ABreakpoints(breakpoints)
PreConfigureHook, RunHook, SeekHook, UInfos, UParameterTweakInfos, ValidateHook, ) from ..infos import ( DatasetProducedInfo, DetectorMutiframeInfo, ParameterTweakInfo, RunProgressInfo, ) from ..util import ADetectorTable, DetectorTable, RunnableStates with Anno("The initial value of FramesPerStep for this detector at configure"): AInitialFramesPerStep = int # Pull re-used annotypes into our namespace in case we are subclassed AMri = builtin.parts.AMri AInitialVisibility = builtin.parts.AInitialVisibility ss = RunnableStates class DetectorChildPart(builtin.parts.ChildPart): """Part controlling a child detector Block that exposes a configure/run interface with fileDir and fileTemplate""" def __init__( self, name: APartName,
from annotypes import Anno from malcolm.core import ( Alarm, ChoiceMeta, NumberMeta, Part, PartRegistrar, Port, StringMeta, ) from malcolm.modules import builtin, ca from ..util import CS_AXIS_NAMES with Anno("PV prefix for CSPort and CSAxis records"): APvPrefix = str # Pull re-used annotypes into our namespace in case we are subclassed AGroup = ca.util.AGroup class RawMotorSinkPortsPart(Part): """Defines a string `Attribute` representing a asyn port that should be depicted as a Source Port on a Block""" def __init__(self, pv_prefix: APvPrefix, group: ca.util.AGroup = None) -> None: super().__init__("sinkPorts") self.pvs = [pv_prefix + ":CsPort", pv_prefix + ":CsAxis"] self.rbvs = [
import unittest import os from annotypes import add_call_types, Anno from scanpointgenerator import LineGenerator, CompoundGenerator from malcolm.core import Part, Process, Context, APartName, PartRegistrar, \ NumberMeta from malcolm.modules.builtin.hooks import AContext from malcolm.modules.builtin.util import set_tags from malcolm.modules.scanning.hooks import RunHook from malcolm.modules.scanning.parts import RunnableChildPart from malcolm.modules.scanning.controllers import RunnableController with Anno("How long to wait"): AWait = float class WaitingPart(Part): def __init__(self, name, wait=0.0): # type: (APartName, AWait) -> None super(WaitingPart, self).__init__(name) meta = NumberMeta("float64", "How long to wait") set_tags(meta, writeable=True) self.attr = meta.create_attribute_model(wait) self.register_hooked(RunHook, self.run) def setup(self, registrar): # type: (PartRegistrar) -> None registrar.add_attribute_model(self.name, self.attr, self.attr.set_value)
import os from annotypes import Anno, add_call_types from tornado.web import RedirectHandler, StaticFileHandler from malcolm.core import APartName, Part, PartRegistrar from ..hooks import ReportHandlersHook, UHandlerInfos from ..infos import HandlerInfo www_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "www")) with Anno("Path to www directory to get files from"): APath = str # Always serve index.html, no matter what the route class IndexHandler(StaticFileHandler): @classmethod def get_absolute_path(cls, root, path): return super(IndexHandler, cls).get_absolute_path(root, "index.html") class GuiServerPart(Part): """Static file server to be used as a fallback. Must be last part""" GuiHandler = IndexHandler def __init__(self, name: APartName = "gui", path: APath = www_dir) -> None: super().__init__(name) self.path = path
from annotypes import Anno, WithCallTypes, Array, to_array, Union, Sequence with Anno("The scannable axes, e.g. ['x', 'y'] or 'x'"): Axes = Array[str] with Anno("The first point to be generated, e.g. [0., 2.4] or 1."): Start = Array[float] with Anno("The final point to be generated, e.g. [-8., 6.4] or 5."): Stop = Array[float] with Anno("The number of points to generate, e.g. 5"): Size = int with Anno("The scannable units, e.g. ['mm', 'deg'] or 'mm'"): Units = Array[str] with Anno("Whether to reverse on alternate runs"): Alternate = bool def_units = Units("mm") class ManyArgs(WithCallTypes): def __init__(self, axes: Union[Axes, Sequence[str], str], start: Union[Start, Sequence[float], float], stop: Union[Stop, Sequence[float], float], size: Size, units: Union[Units, Sequence[str], str] = def_units, alternate: Alternate = False): self.axes = Axes(axes) self.start = Start(start) self.stop = Stop(stop) self.size = size self.units = Units(units)
from annotypes import Anno from malcolm.core import ( Alarm, BooleanMeta, Part, PartRegistrar, TimeStamp, VMeta, snake_to_camel, ) from ..pandablocksclient import PandABlocksClient with Anno("Client for setting and getting field"): AClient = PandABlocksClient with Anno("Meta object to create attribute from"): AMeta = VMeta with Anno("Name of Block in TCP server"): ABlockName = str with Anno("Name of Field in TCP server"): AFieldName = str with Anno("Initial value of attribute"): AInitialValue = Any class PandAFieldPart(Part): """This will normally be instantiated by the PandABox assembly, not created in yaml""" def __init__(
from annotypes import Anno, add_call_types from malcolm.core import PartRegistrar from malcolm.modules.builtin.hooks import AContext from malcolm.modules.builtin.parts import AMri, APartName, ChildPart from .. import hooks with Anno("Whether to raise a ValueError for a bad status"): AErrorOnFail = bool class DirectoryMonitorPart(ChildPart): """Part for checking a directoryMonitor Manager is happy""" def __init__(self, name: APartName, mri: AMri, error_on_fail: AErrorOnFail = True) -> None: super().__init__(name, mri, initial_visibility=True) self.error_on_fail = error_on_fail def setup(self, registrar: PartRegistrar) -> None: super().setup(registrar) # Hooks registrar.hook(hooks.ConfigureHook, self.check_directories) @add_call_types def check_directories(self, context: AContext) -> None: child = context.block_view(self.mri) try: child.managerCheck()
# which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # # Contributors: # Charles Mita - initial API and implementation and/or initial documentation # ### from annotypes import Anno, Union, Array, Sequence from math import cos, sin from scanpointgenerator.core import ROI from scanpointgenerator.compat import np with Anno("The start point of the rectangle"): AStart = Array[float] UStart = Union[AStart, Sequence[float]] with Anno("The width of the rectangle"): AWidth = float with Anno("The height of the rectangle"): AHeight = float with Anno("The angle of the rectangle"): AAngle = float @ROI.register_subclass("scanpointgenerator:roi/RectangularROI:1.0") class RectangularROI(ROI): def __init__(self, start, width, height, angle=0): # type: (UStart, AWidth, AHeight, AAngle) -> None super(RectangularROI, self).__init__()
class StatisticsName(Enum): MIN = "MIN_VALUE" # Minimum counts in any element MIN_X = "MIN_X" # X position of minimum counts MIN_Y = "MIN_Y" # Y position of minimum counts MAX = "MAX_VALUE" # Maximum counts in any element MAX_X = "MAX_X" # X position of maximum counts MAX_Y = "MAX_Y" # Y position of maximum counts MEAN = "MEAN_VALUE" # Mean counts of all elements SIGMA = "SIGMA_VALUE" # Sigma of all elements SUM = "TOTAL" # Sum of all elements NET = "NET" # Sum of all elements not in background region with Anno("Dataset names"): ANameArray = Array[str] with Anno("Filenames of HDF files relative to fileDir"): AFilenameArray = Array[str] with Anno("Types of dataset"): ATypeArray = Array[DatasetType] with Anno("Rank (number of dimensions) of the dataset"): ARankArray = Array[np.int32] with Anno("Dataset paths within HDF files"): APathArray = Array[str] with Anno("UniqueID array paths within HDF files"): AUniqueIDArray = Array[str] UNameArray = Union[ANameArray, Sequence[str]] UFilenameArray = Union[AFilenameArray, Sequence[str]] UTypeArray = Union[ATypeArray, Sequence[DatasetType]] URankArray = Union[ARankArray, Sequence[np.int32]]
import time from annotypes import Anno, WithCallTypes with Anno("The exposure to be active for"): Exposure = float with Anno("The full path to the text file to write"): Path = str class Simple(WithCallTypes): def __init__(self, exposure, path="/tmp/file.txt"): # type: (Exposure, Path) -> None self.exposure = exposure self.path = path def write_data(self, data): # type: (str) -> None with open(self.path, "w") as f: time.sleep(self.exposure) f.write("Data: %s\n" % data)
class StatisticsName(Enum): """The types of statistics calculated by the areaDetector NDPluginStats""" MIN = "MIN_VALUE" #: Minimum counts in any element MIN_X = "MIN_X" #: X position of minimum counts MIN_Y = "MIN_Y" #: Y position of minimum counts MAX = "MAX_VALUE" #: Maximum counts in any element MAX_X = "MAX_X" #: X position of maximum counts MAX_Y = "MAX_Y" #: Y position of maximum counts MEAN = "MEAN_VALUE" #: Mean counts of all elements SIGMA = "SIGMA_VALUE" #: Sigma of all elements SUM = "TOTAL" #: Sum of all elements NET = "NET" #: Sum of all elements not in background region with Anno("Is the IOC this part connects to running on Windows?"): APartRunsOnWindows = bool with Anno("NDAttribute name to be exported"): AAttributeNames = Union[Array[str]] with Anno("source ID for attribute (PV name for PVAttribute," + "asyn param name for paramAttribute)"): ASourceIds = Union[Array[str]] with Anno("PV descriptions"): ADescriptions = Union[Array[str]] with Anno("Types of attribute dataset"): AAttributeTypes = Union[Array[AttributeDatasetType]] with Anno("Type of attribute source"): ASourceTypes = Union[Array[SourceType]] with Anno("Type of attribute data"): ADataTypes = Union[Array[DataType]]
from annotypes import Anno from malcolm.modules import builtin from ..util import AClient with Anno("The field of *METADATA to set when label is changed"): AMetadataField = str ALabelValue = builtin.parts.ALabelValue class PandALabelPart(builtin.parts.LabelPart): def __init__(self, client: AClient, metadata_field: AMetadataField, value: ALabelValue) -> None: super().__init__(value) self.client = client self.metadata_field = metadata_field def handle_change(self, value, ts): if not value: value = self.initial_value super().set_label(value, ts) def set_label(self, value, ts=None): super().set_label(value, ts) self.client.set_field("*METADATA", self.metadata_field, self.attr.value)
def test_put_attribute_values(self): self.o.put_attribute_values(dict(attr=43)) self.context.put_async.assert_called_once_with( ["block", "attr", "value"], 43) self.context.wait_all_futures.assert_called_once_with( [self.context.put_async.return_value], timeout=None, event_timeout=None) def test_async_call(self): self.o.method_async(a=3) self.o.method.post_async.assert_called_once_with(a=3) with Anno("A Param"): AParam = str class MyPart(Part): def setup(self, registrar): registrar.add_method_model(self.my_method, "myMethod") @add_call_types def my_method(self, param1: AParam, param2: AParam) -> AParam: return param1 + param2 class TestMethod(unittest.TestCase): def setUp(self): self.process = Process("proc")
PreConfigureHook, PreRunHook, ReportStatusHook, RunHook, SeekHook, ValidateHook, ) from ..infos import ConfigureParamsInfo, ParameterTweakInfo, RunProgressInfo from ..util import AGenerator, ConfigureParams, RunnableStates, resolve_generator_tweaks PartContextParams = Iterable[Tuple[Part, Context, Dict[str, Any]]] PartConfigureParams = Dict[Part, ConfigureParamsInfo] ss = RunnableStates with Anno("The validated configure parameters"): AConfigureParams = ConfigureParams with Anno("Step to mark as the last completed step, -1 for current"): ALastGoodStep = int # Pull re-used annotypes into our namespace in case we are subclassed AConfigDir = builtin.controllers.AConfigDir AInitialDesign = builtin.controllers.AInitialDesign ADescription = builtin.controllers.ADescription ATemplateDesigns = builtin.controllers.ATemplateDesigns def update_configure_model( configure_model: MethodMeta, part_configure_infos: List[ConfigureParamsInfo] ) -> None: # These will not be inserted as they already exist
from annotypes import Anno from malcolm.core import Part, PartRegistrar, StringMeta from ..infos import LabelInfo from ..util import set_tags with Anno("Initial value of Block label"): ALabelValue = str class LabelPart(Part): """Part representing a the title of the Block a GUI should display""" def __init__(self, value: ALabelValue = None) -> None: super().__init__("label") meta = StringMeta("Label for the block") set_tags(meta, writeable=True) self.initial_value = value self.attr = meta.create_attribute_model(self.initial_value) def _report(self): self.registrar.report(LabelInfo(self.attr.value)) def setup(self, registrar: PartRegistrar) -> None: super().setup(registrar) registrar.add_attribute_model(self.name, self.attr, self.set_label) self._report() def set_label(self, value, ts=None): self.attr.set_value(value, ts=ts)
import time from annotypes import Anno, add_call_types from malcolm.core import PartRegistrar from malcolm.modules import builtin # Pull re-used annotypes into our namespace in case we are subclassed APartName = builtin.parts.APartName AMri = builtin.parts.AMri with Anno("The demand value to move our counter motor to"): ADemand = float with Anno("The amount of time to get to the demand position"): ADuration = float class MotorMovePart(builtin.parts.ChildPart): """Provides control of a `counter_block` within a `ManagerController`""" def __init__(self, name, mri): # type: (APartName, AMri) -> None super(MotorMovePart, self).__init__(name, mri, stateful=False, initial_visibility=True) def setup(self, registrar): # type: (PartRegistrar) -> None super(MotorMovePart, self).setup(registrar) # Method registrar.add_method_model(self.move,
def get_motion_trigger( part_info: scanning.hooks.APartInfo, ) -> scanning.infos.MotionTrigger: infos: List[ MotionTriggerInfo] = scanning.infos.MotionTriggerInfo.filter_values( part_info) if infos: assert len( infos) == 1, f"Expected 0 or 1 MotionTriggerInfo, got {len(infos)}" trigger = infos[0].trigger else: trigger = scanning.infos.MotionTrigger.EVERY_POINT return trigger with Anno("Minimum turnaround time for non-joined points"): AMinTurnaround = float with Anno("Minimum interval between turnaround points"): AMinInterval = float @dataclass class MinTurnaround: """Dataclass for the Minimum turnaround information. This may come from a MinTurnaroundInfo if the scan block has a MinTurnaroundPart otherwise MIN_TIME and MIN_INTERVAL are used as default values. """ time: AMinTurnaround interval: AMinInterval
from typing import Any from annotypes import Anno, WithCallTypes with Anno("The name of the defined parameter"): AName = str with Anno("The value of the defined parameter"): AValue = Any class Define(WithCallTypes): def __init__(self, name: AName, value: AValue) -> None: self.name = name self.value = value
from .loggable import Loggable T = TypeVar("T") # Clear spawned handles after how many spawns? SPAWN_CLEAR_COUNT = 1000 # States for how far in start procedure we've got STOPPED = 0 STARTING = 1 STARTED = 2 STOPPING = 3 with Anno("The list of currently published Controller mris"): APublished = Union[Array[str]] class UnpublishedInfo(Info): def __init__(self, mri: str) -> None: self.mri = mri class ProcessPublishHook(Hook): """Called when a new block is added""" def __init__(self, child: AHookable, published: APublished) -> None: super().__init__(child, published=published)
from annotypes import Anno, Sequence, Array, Union from malcolm.core import Part, snake_to_camel, PartRegistrar from ..pandablocksclient import PandABlocksClient with Anno("Client for setting and getting field"): AClient = PandABlocksClient with Anno("Name of Block in TCP server"): ABlockName = str with Anno("Name of Field in TCP server"): AFieldName = str with Anno("Description for the Method"): ADescription = str with Anno("Tags to be attached to Method"): ATags = Array[str] UTags = Union[ATags, Sequence[str], str] class PandABlocksActionPart(Part): """This will normally be instantiated by the PandABox assembly, not created in yaml""" def __init__(self, client, block_name, field_name, description, tags): # type: (AClient, ABlockName, AFieldName, ADescription, ATags) -> None super(PandABlocksActionPart, self).__init__(field_name) self.client = client self.block_name = block_name self.field_name = field_name self.description = description self.tags = tags self.method = None
from annotypes import Anno, WithCallTypes, Array, add_call_types class Table(WithCallTypes): def validate(self): lengths = {a: len(getattr(self, a)) for a in self.call_types} assert len(set(lengths.values())) == 1, \ "Column lengths %s don't match" % lengths def __getitem__(self, item): self.validate() return [getattr(self, a)[item] for a in self.call_types] with Anno("Name of layout part"): Name = Array[str] with Anno("Malcolm full name of child block"): MRI = Array[str] with Anno("X Coordinate of child block"): X = Array[float] with Anno("Y Coordinate of child block"): Y = Array[float] with Anno("Whether child block is visible"): Visible = Array[bool] class LayoutTable(Table): def __init__(self, name: Name, mri: MRI, x: X, y: Y, visible: Visible): self.name = name self.mri = mri self.x = x
import os from xml.etree import cElementTree as ET from annotypes import Anno, add_call_types from malcolm.compat import et_to_string from malcolm.core import APartName, PartRegistrar from malcolm.modules import builtin, scanning from ..infos import CalculatedNDAttributeDatasetInfo, FilePathTranslatorInfo from ..util import APartRunsOnWindows, StatisticsName, make_xml_filename with Anno("Which statistic to capture"): AStatsName = StatisticsName with Anno("Directory to write data to"): AFileDir = str # Pull re-used annotypes into our namespace in case we are subclassed APartName = APartName # We will set these attributes on the child block, so don't save them @builtin.util.no_save("attributesFile", "enableCallbacks", "computeStatistics", "arrayCounter") class StatsPluginPart(builtin.parts.ChildPart): """Part for controlling a `stats_plugin_block` in a Device""" def __init__( self, name: APartName, mri: builtin.parts.AMri, statistic: AStatsName = StatisticsName.SUM,
import logging from annotypes import Anno, Array, Any, TYPE_CHECKING, Mapping, Union, Sequence from .response import Return, Error, Update, Delta, Response from .serializable import Serializable, serialize_object if TYPE_CHECKING: from typing import Callable, Tuple, List Callback = Callable[[Response], None] # Create a module level logger log = logging.getLogger(__name__) with Anno("ID that should be used for any responses"): AId = int with Anno("Path to target Block substructure"): APath = Array[str] with Anno("Value to put"): AValue = Any with Anno("If set then return the current value in Return when Put completes"): AGet = bool with Anno("Parameters to use in a method Post"): AParameters = Mapping[str, Any] with Anno("Notify of differences only"): ADifferences = bool UPath = Union[APath, Sequence[str], str] class Request(Serializable): """Request object that registers a callback for when action is complete."""
from annotypes import Anno from malcolm.core import Part, PartRegistrar, NumberMeta, APartName, \ AMetaDescription from ..util import set_tags, AWriteable, AConfig, AGroup, AWidget with Anno("Initial value of the created attribute"): Value = float class Float64Part(Part): """Create a single float64 Attribute on the Block""" def __init__( self, name, # type: APartName description, # type: AMetaDescription writeable=False, # type: AWriteable config=1, # type: AConfig group=None, # type: AGroup widget=None, # type: AWidget value=0.0, # type: Value ): # type: (...) -> None super(Float64Part, self).__init__(name) meta = NumberMeta("float64", description) set_tags(meta, writeable, config, group, widget) self.attr = meta.create_attribute_model(value) self.writeable_func = self.attr.set_value if writeable else None def setup(self, registrar): # type: (PartRegistrar) -> None