def __init__(self, directories): """Initialise the FileInterface object. This constructor initialises the FileInterface object, building a parameter tree. """ # Save arguments self.fp_config_files = [] self.txt_files = [] self.fr_config_files = [] self.directories = directories self.odin_data_config_dir = directories["odin_data"] # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Store all information in a parameter tree self.param_tree = ParameterTree({ 'odin_version': version_info['version'], 'tornado_version': tornado.version, 'server_uptime': (self.get_server_uptime, None), 'fr_config_files': (self.get_fr_config_files, None), 'fp_config_files': (self.get_fp_config_files, None), 'config_dir': (self.odin_data_config_dir, None) })
def __init__(self): self.int_rw_param = 4576 self.int_ro_param = 255374 self.int_rw_value = 9876 self.int_wo_param = 0 self.rw_value_set_called = False self.nested_rw_param = 53.752 self.nested_ro_value = 9.8765 nested_tree = ParameterTree({ 'nestedRwParam': (self.nestedRwParamGet, self.nestedRwParamSet), 'nestedRoParam': self.nested_ro_value }) self.rw_callable_tree = ParameterTree({ 'intCallableRwParam': (self.intCallableRwParamGet, self.intCallableRwParamSet), 'intCallableRoParam': (self.intCallableRoParamGet, None), 'intCallableWoParam': (None, self.intCallableWoParamSet), 'intCallableRwValue': (self.int_rw_value, self.intCallableRwValueSet), 'branch': nested_tree })
def __init__(self): """Initialise the SystemInfo object. This constructor initlialises the SystemInfo object, extracting various system-level parameters and storing them in a parameter tree to be accessible to clients. """ # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Extract platform information and store in parameter tree (system, node, release, version, machine, processor) = platform.uname() platform_tree = ParameterTree({ 'system': system, 'node': node, 'release': release, 'version': version, 'processor': processor }) # Store all information in a parameter tree self.param_tree = ParameterTree({ 'odin_version': version_info['version'], 'tornado_version': tornado.version, 'platform': platform_tree, 'server_uptime': (self.get_server_uptime, None), })
def setup_class(cls): cls.int_rw_param = 4576 cls.int_ro_param = 255374 cls.int_rw_value = 9876 cls.int_wo_param = 0 cls.rw_value_set_called = False cls.nested_rw_param = 53.752 cls.nested_ro_value = 9.8765 nested_tree = ParameterTree({ 'nestedRwParam': (cls.nestedRwParamGet, cls.nestedRwParamSet), 'nestedRoParam': cls.nested_ro_value }) cls.rw_callable_tree = ParameterTree({ 'intCallableRwParam': (cls.intCallableRwParamGet, cls.intCallableRwParamSet), 'intCallableRoParam': (cls.intCallableRoParamGet, None), 'intCallableWoParam': (None, cls.intCallableWoParamSet), 'intCallableRwValue': (cls.int_rw_value, cls.intCallableRwValueSet), 'branch': nested_tree })
def test_mutable_nested_tree_external_change(self, test_tree_mutable): new_tree = ParameterTree({ 'immutable_param': "Hello", "tree": test_tree_mutable.param_tree }) new_node = {"new": 65} path = 'tree/extra' test_tree_mutable.param_tree.set('extra', new_node) val = new_tree.get(path) assert val['extra'] == new_node
def __init__(self, **kwargs): """ Initialise the ProxyAdapter. This constructor initialises the adapter instance, parsing configuration options out of the keyword arguments it is passed. A ProxyTarget object is instantiated for each target specified in the options. :param kwargs: keyword arguments specifying options """ # Initialise base class super(ProxyAdapter, self).__init__(**kwargs) # Set the HTTP request timeout if present in the options request_timeout = None if TIMEOUT_CONFIG_NAME in self.options: try: request_timeout = float(self.options[TIMEOUT_CONFIG_NAME]) logging.debug('ProxyAdapter request timeout set to %f secs', request_timeout) except ValueError: logging.error( "Illegal timeout specified for ProxyAdapter: %s", self.options[TIMEOUT_CONFIG_NAME] ) # Parse the list of target-URL pairs from the options, instantiating a ProxyTarget # object for each target specified. self.targets = [] if TARGET_CONFIG_NAME in self.options: for target_str in self.options[TARGET_CONFIG_NAME].split(','): try: (target, url) = target_str.split('=') self.targets.append(ProxyTarget(target.strip(), url.strip(), request_timeout)) except ValueError: logging.error("Illegal target specification for ProxyAdapter: %s", target_str.strip()) # Issue an error message if no targets were loaded if self.targets: logging.debug("ProxyAdapter with {:d} targets loaded".format(len(self.targets))) else: logging.error("Failed to resolve targets for ProxyAdapter") # Construct the parameter tree returned by this adapter tree = {'status': {}} for target in self.targets: tree['status'][target.name] = target.status_param_tree tree[target.name] = target.data_param_tree self.param_tree = ParameterTree(tree)
def test_mutable_nested_tree_in_immutable_tree(self, test_tree_mutable): new_tree = ParameterTree({ 'immutable_param': "Hello", "nest": { "tree": test_tree_mutable.param_tree } }) new_node = {"new": 65} path = 'nest/tree/extra' new_tree.set(path, new_node) val = new_tree.get(path) assert val['extra'] == new_node
def __init__(self): # param tree thats set to mutable, with some nodes i guess? # make sure to test the different types of node being added/overwritten (inc param-accessor) self.read_value = 64 self.write_value = "test" self.param_tree_dict = { 'extra': 'wibble', 'bonus': 'win', 'nest': { 'double_nest': { 'nested_val': 125, 'dont_touch': "let me stay!", 'write': (self.get_write, self.set_write) }, 'list': [0, 1, { 'list_test': "test" }, 3] }, 'read': (self.get_read, ), 'empty': {} } self.param_tree = ParameterTree(self.param_tree_dict) self.param_tree.mutable = True
def test_bad_tuple_node_raises_error(self): bad_node = 'bad' bad_data = tuple(range(4)) bad_tree = {bad_node: bad_data} with assert_raises_regexp(ParameterTreeError, "not a valid leaf node"): tree = ParameterTree(bad_tree)
def __init__(self, *args, **kwargs): """Initilise the QuadData instance. This constructor initialises the QuadData instance. If an existing Quad instance is passed in as a keyword argument, that is used for accessing data, otherwise a new instance is created. :param args: positional arguments to be passed if creating a new Quad :param kwargs: keyword arguments to be passed if creating a new Quad, or if a quad key is present, that is used as an existing quad object instance """ # Is a quad has been passed in keyword arguments use that, otherwise create a new one if 'quad' in kwargs: self.quad = kwargs['quad'] else: self.quad = Quad(*args, **kwargs) # Create data containers for all channels on the quad self.channels = [ ChannelData(self.quad, i) for i in range(self.quad.num_channels) ] # Build the parameter tree self.param_tree = ParameterTree({ "channels": [ self.channels[0].param_tree, self.channels[1].param_tree, self.channels[2].param_tree, self.channels[3].param_tree ], "supply": (self.get_supply_voltage, None), })
def setup_class(cls): cls.int_rw_param = 100 cls.float_ro_param = 4.6593 cls.int_ro_param = 1000 cls.int_enum_param = 0 cls.int_enum_param_allowed_values = [0, 1, 2, 3, 5, 8, 13] cls.int_rw_param_metadata = { "min": 0, "max": 1000, "units": "arbitrary", "name": "intCallableRwParam", "description": "A callable integer RW parameter" } cls.metadata_tree_dict = { 'name': 'Metadata Tree', 'description': 'A paramter tree to test metadata', 'floatRoParam': (cls.floatRoParamGet, ), 'intRoParam': (cls.intRoParamGet, { "units": "seconds" }), 'intCallableRwParam': (cls.intCallableRwParamGet, cls.intCallableRwParamSet, cls.int_rw_param_metadata), 'intEnumParam': (0, { "allowed_values": cls.int_enum_param_allowed_values }), 'valueParam': (24601, ) } cls.metadata_tree = ParameterTree(cls.metadata_tree_dict)
def __init__(self, fem_id, host_addr, port, data_addr=None, fem=None, state=STATE_DISCONNECTED): self.fem_id = fem_id self.host_addr = host_addr self.port = port self.data_addr = data_addr if data_addr is not None else self.DEFAULT_DATA_ADDR self.fem = fem self.set_chip_enable(self.DEFAULT_CHIP_ENABLE_MASK) self.state = self.STATE_DISCONNECTED self.connected = False self.error_code = FEM_RTN_OK self.error_msg = "" self.timeout_ms = self.DEFAULT_TIMEOUT_MS self.param_tree = ParameterTree({ 'id': (self._get('fem_id'), None), 'address': (self._get('host_addr'), None), 'port': (self._get('port'), None), 'data_address': (self._get('data_addr'), None), 'timeout_ms': (self._get('timeout_ms'), None), 'chip_enable_mask': (self._get('chip_enable_mask'), None), 'chips_enabled': (self._get('chips_enabled'), None), 'state': (self._get('state'), None), 'connected': (self._get('connected'), None), 'error_code': (self._get('error_code'), None), 'error_msg': (self._get("error_msg"), None), })
def __init__(self, name, endpoint, drop_warn_cutoff, callback): """ Initialise a Node for the Adapter. The node should subscribe to a ZMQ socket and pass any frames received at that socket to the main adapter. """ logging.debug("Live View Proxy Node Initialising: %s", endpoint) self.name = name self.endpoint = endpoint self.callback = callback self.dropped_frame_count = 0 self.received_frame_count = 0 self.last_frame = 0 self.current_acq = 0 self.drop_warn_cutoff = drop_warn_cutoff self.drop_unwarn_cutoff = drop_warn_cutoff * 0.75 self.has_warned = False # subscribe to the given socket address. self.channel = IpcTornadoChannel(IpcTornadoChannel.CHANNEL_TYPE_SUB, endpoint=endpoint) self.channel.subscribe() self.channel.connect() # callback is called whenever data 'arrives' at the socket. This is driven by the IOLoop self.channel.register_callback(self.local_callback) self.param_tree = ParameterTree({ 'endpoint': (lambda: self.endpoint, None), 'dropped_frames': (lambda: self.dropped_frame_count, None), 'received_frames': (lambda: self.received_frame_count, None), 'last_frame': (lambda: self.last_frame, None), 'current_acquisition': (lambda: self.current_acq, None), }) logging.debug("Proxy Connected to Socket: %s", endpoint)
def test_mutable_nested_tree_root_tree_not_affected( self, test_tree_mutable): new_tree = ParameterTree({ 'immutable_param': "Hello", "nest": { "tree": test_tree_mutable.param_tree } }) new_node = {"new": 65} path = 'immutable_param' with pytest.raises(ParameterTreeError) as excinfo: new_tree.set(path, new_node) assert "Type mismatch" in str(excinfo.value)
def setup_class(cls): cls.int_value = 1234 cls.float_value = 3.1415 cls.bool_value = True cls.str_value = 'theString' cls.list_values = list(range(4)) cls.simple_dict = { 'intParam': cls.int_value, 'floatParam': cls.float_value, 'boolParam': cls.bool_value, 'strParam': cls.str_value, } cls.simple_tree = ParameterTree(cls.simple_dict) # Set up nested dict of parameters for a more complex tree cls.nested_dict = cls.simple_dict.copy() cls.nested_dict['branch'] = { 'branchIntParam': 4567, 'branchStrParam': 'theBranch', } cls.nested_tree = ParameterTree(cls.nested_dict) cls.callback_tree = deepcopy(cls.nested_tree) cls.callback_tree.add_callback('branch/', cls.branch_callback) cls.branch_callback_count = 0 cls.complex_tree_branch = ParameterTree(deepcopy(cls.nested_dict)) cls.complex_tree_branch.add_callback('', cls.branch_callback) cls.complex_tree = ParameterTree({ 'intParam': cls.int_value, 'callableRoParam': (lambda: cls.int_value, None), 'listParam': cls.list_values, 'branch': cls.complex_tree_branch, }) cls.list_tree = ParameterTree( {'main': [cls.simple_dict.copy(), list(cls.list_values)]})
def __init__(self, file_name, file_dir): self.file_dir = file_dir self.file_name = file_name self.vector_loop_position = 0 self.vector_length = 0 self.vector_names = [] self.vector_data = [] self.dac_clock_refs = [] self.dac_data_vector = [] self.bias = {} self.clock_step = 0 self.get_vector_information() self.extract_clock_references() self.convert_raw_dac_data() self.bias_tree = ParameterTree( # dict comprehension, like a one-line for loop # basically, for each bias, create a tuple of partial functions # that get/set values from the dictionary { bias_name: ( partial(self.get_bias_val, bias_name), partial(self.set_bias_val, bias_name), # metadata ensures the bias val can't go over its 6 bit max { "min": 0, "max": (2**self.BIAS_DEPTH) - 1 }) for bias_name in self.bias.keys() }) self.param_tree = ParameterTree({ "bias": self.bias_tree, "file_name": (lambda: self.file_name, self.set_file_name), "file_dir": (lambda: self.file_dir, None), "length": (lambda: self.vector_length, None), "loop_pos": (lambda: self.vector_loop_position, None), "save": (None, self.write_vector_file), "reset": (None, self.reset_vector_file) })
def test_bad_tuple_node_raises_error(self, test_param_tree): """Test that constructing a parameter tree with an immutable tuple raises an error.""" bad_node = 'bad' bad_data = tuple(range(4)) bad_tree = {bad_node: bad_data} with pytest.raises(ParameterTreeError) as excinfo: tree = ParameterTree(bad_tree) assert "not a valid leaf node" in str(excinfo.value)
def test_mutable_nested_tree_delete(self, test_tree_mutable): new_tree = ParameterTree({ 'immutable_param': "Hello", "tree": test_tree_mutable.param_tree }) path = 'tree/bonus' new_tree.delete(path) tree = new_tree.get('') assert 'bonus' not in tree['tree'] with pytest.raises(ParameterTreeError) as excinfo: test_tree_mutable.param_tree.get(path) assert "Invalid path" in str(excinfo.value)
def __init__(self, background_task_enable, background_task_interval): """Initialise the Workshop object. This constructor initlialises the Workshop object, building a parameter tree and launching a background task if enabled """ # Save arguments self.background_task_enable = background_task_enable self.background_task_interval = background_task_interval # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Build a parameter tree for the background task bg_task = ParameterTree({ 'count': (lambda: self.background_task_counter, None), 'enable': (lambda: self.background_task_enable, self.set_task_enable), 'interval': (lambda: self.background_task_interval, self.set_task_interval), }) # Store all information in a parameter tree self.param_tree = ParameterTree({ 'odin_version': version_info['version'], 'tornado_version': tornado.version, 'server_uptime': (self.get_server_uptime, None), 'background_task': bg_task }) # Set the background task counter to zero self.background_task_counter = 0 # Launch the background task if enabled in options if self.background_task_enable: logging.debug("Launching background task with interval %.2f secs", background_task_interval) self.background_task()
def __init__(self, name, url, request_timeout): """ Initialise the BaseProxyTarget object. Sets up the default state of the base target object, builds the appropriate parameter tree to be handled by the containing adapter and sets up the HTTP client for making requests to the target. :param name: name of the proxy target :param url: URL of the remote target :param request_timeout: request timeout in seconds """ self.name = name self.url = url self.request_timeout = request_timeout # Initialise default state self.status_code = 0 self.error_string = 'OK' self.last_update = 'unknown' self.data = {} self.metadata = {} self.counter = 0 # Build a parameter tree representation of the proxy target status self.status_param_tree = ParameterTree({ 'url': (lambda: self.url, None), 'status_code': (lambda: self.status_code, None), 'error': (lambda: self.error_string, None), 'last_update': (lambda: self.last_update, None), }) # Build a parameter tree representation of the proxy target data self.data_param_tree = ParameterTree((lambda: self.data, None)) self.meta_param_tree = ParameterTree((lambda: self.metadata, None)) # Set up default request headers self.request_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', }
class SystemInfo(with_metaclass(Singleton, object)): """SystemInfo - class that extracts and stores information about system-level parameters.""" # __metaclass__ = Singleton def __init__(self): """Initialise the SystemInfo object. This constructor initlialises the SystemInfo object, extracting various system-level parameters and storing them in a parameter tree to be accessible to clients. """ # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Extract platform information and store in parameter tree (system, node, release, version, machine, processor) = platform.uname() platform_tree = ParameterTree({ 'system': system, 'node': node, 'release': release, 'version': version, 'processor': processor }) # Store all information in a parameter tree self.param_tree = ParameterTree({ 'odin_version': version_info['version'], 'tornado_version': tornado.version, 'platform': platform_tree, 'server_uptime': (self.get_server_uptime, None), }) def get_server_uptime(self): """Get the uptime for the ODIN server. This method returns the current uptime for the ODIN server. """ return time.time() - self.init_time def get(self, path): """Get the parameter tree. This method returns the parameter tree for use by clients via the SystemInfo adapter. :param path: path to retrieve from tree """ return self.param_tree.get(path)
def __init__(self, name, url, request_timeout): """ Initalise the ProxyTarget object. Sets up the default state of the target object, builds the appropriate parameter tree to be handled by the containing adapter and sets up the HTTP client for making requests to the target. """ self.name = name self.url = url self.request_timeout = request_timeout # Initialise default state self.status_code = 0 self.error_string = 'OK' self.last_update = 'unknown' self.data = {} self.metadata = {} self.counter = 0 # Build a parameter tree representation of the proxy target status self.status_param_tree = ParameterTree({ 'url': (lambda: self.url, None), 'status_code': (lambda: self.status_code, None), 'error': (lambda: self.error_string, None), 'last_update': (lambda: self.last_update, None), }) # Build a parameter tree representation of the proxy target data self.data_param_tree = ParameterTree((lambda: self.data, None)) self.meta_param_tree = ParameterTree((lambda: self.metadata, None)) # Create an HTTP client instance and set up default request headers self.http_client = tornado.httpclient.HTTPClient() self.request_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', } self.remote_get() # init the data tree self.remote_get(get_metadata=True) # init the metadata
class ProxyTestHandler(RequestHandler): """ Tornado request handler for use in test server needed for proxy tests.""" # Data structure served by request handler data = { 'one': (1, None), # this allows for auto generated metadata for testing purposes 'two': 2.0, 'pi': 3.14, 'more': { 'three': 3.0, 'replace': 'Replace Me!', 'even_more': { 'extra_val': 5.5 } } } param_tree = ParameterTree(data) def initialize(self, server): """Increment the server access count every time the request handler is invoked.""" server.access_count += 1 def get(self, path=''): """Handle GET requests to the test server.""" try: data_ref = self.param_tree.get(path, wants_metadata(self.request)) self.write(data_ref) except ParameterTreeError: self.set_status(404) self.write_error(404) except Exception as other_e: logging.error("ProxyTestHandler GET failed: %s", str(other_e)) self.write_error(500) def put(self, path): """Handle PUT requests to the test server.""" response_body = convert_unicode_to_string( tornado.escape.json_decode(self.request.body)) try: self.param_tree.set(path, response_body) data_ref = self.param_tree.get(path) self.write(data_ref) except ParameterTreeError: self.set_status(404) self.write_error(404) except Exception as other_e: logging.error("ProxyTestHandler PUT failed: %s", str(other_e)) self.write_error(500)
def __init__(self): self.int_value = 1234 self.float_value = 3.1415 self.bool_value = True self.str_value = 'theString' self.list_values = list(range(4)) self.simple_dict = { 'intParam': self.int_value, 'floatParam': self.float_value, 'boolParam': self.bool_value, 'strParam': self.str_value, } self.accessor_params = {'one': 1, 'two': 2, 'pi': 3.14} self.simple_tree = ParameterTree(self.simple_dict) # Set up nested dict of parameters for a more complex tree self.nested_dict = self.simple_dict.copy() self.nested_dict['branch'] = { 'branchIntParam': 4567, 'branchStrParam': 'theBranch', } self.nested_tree = ParameterTree(self.nested_dict) self.callback_tree = deepcopy(self.nested_tree) self.callback_tree.add_callback('branch/', self.branch_callback) self.branch_callback_count = 0 self.complex_tree_branch = ParameterTree(deepcopy(self.nested_dict)) self.complex_tree_branch.add_callback('', self.branch_callback) self.complex_tree = ParameterTree({ 'intParam': self.int_value, 'callableRoParam': (lambda: self.int_value, None), 'callableAccessorParam': (self.get_accessor_param, None), 'listParam': self.list_values, 'branch': self.complex_tree_branch, }) self.list_tree = ParameterTree( {'main': [self.simple_dict.copy(), list(self.list_values)]}) self.simple_list_tree = ParameterTree({'list_param': [10, 11, 12, 13]})
def __init__(self, save_file_dir="", save_file_name="", odin_data_dir=""): self.adapters = {} # self.data_config_dir = config_dir # self.fr_config_file = "" # self.fp_config_file = "" self.file_dir = save_file_dir self.file_name = save_file_name self.odin_data_dir = odin_data_dir self.in_progress = False # these varables used to tell when an acquisiton is completed self.frame_start_acquisition = 0 # number of frames received at start of acq self.frame_end_acquisition = 0 # number of frames at end of acq (start + acq number) logging.debug("ODIN DATA DIRECTORY: %s", self.odin_data_dir) self.process_list = {} self.file_writing = False self.config_dir = "" self.config_files = { "fp": "", "fr": "" } self.param_tree = ParameterTree({ "receiver": { "connected": (self.is_fr_connected, None), "configured": (self.is_fr_configured, None), "config_file": (self.get_fr_config_file, None) }, "processor": { "connected": (self.is_fp_connected, None), "configured": (self.is_fp_configured, None), "config_file": (self.get_fp_config_file, None) }, "file_info": { "enabled": (lambda: self.file_writing, self.set_file_writing), "file_name": (lambda: self.file_name, self.set_file_name), "file_dir": (lambda: self.file_dir, self.set_data_dir) }, "in_progress": (lambda: self.in_progress, None) })
def __init__(self, coarse_calibration_value, fems, daq): self.coarse_calibration_value = coarse_calibration_value self.qem_fems = fems self.qem_daq = daq self.max_calibration = 4096 self.min_calibration = 0 self.calibration_step = 1 self.calibration_value = 0 self.param_tree = ParameterTree({ "start_calibrate": (None, self.adc_calibrate), "start_plot": (None, self.adc_plot), "calibration_vals": { "max": (lambda: self.max_calibration, self.set_max_calib), "min": (lambda: self.min_calibration, self.set_min_calib), "step": (lambda: self.calibration_step, self.set_calib_step), "current": (lambda: self.calibration_value, None) } })
def __init__(self, quad, channel_idx): """Initialise the data container for a Quad channel. This constructor initialises the data container for a LPD Quad output channel, creating the parameter tree and associating it with a specified quad and channel. :param quad: the quad box for this channel :param channel_idx: channel index on the specified quad. """ self.quad = quad self.channel_idx = channel_idx self.param_tree = ParameterTree({ "voltage": (self.get_voltage, None), "current": (self.get_current, None), "fusevoltage": (self.get_fuse, None), "fuseblown": (self.get_fuse_blown, None), "fetfailed": (self.get_fet_failed, None), "enabled": (self.get_enable, self.set_enable), })
def __init__(self, name, url, request_timeout): """ Initalise the ProxyTarget object. Sets up the default state of the target object, builds the appropriate parameter tree to be handled by the containing adapter and sets up the HTTP client for making requests to the target. """ self.name = name self.url = url self.request_timeout = request_timeout # Initialise default state self.status_code = 0 self.error_string = 'OK' self.last_update = 'unknown' self.data = {} self.counter = 0 # Build a parameter tree representation of the proxy target status self.status_param_tree = ParameterTree({ 'url': (self._get_url, None), 'status_code': (self._get_status_code, None), 'error': (self._get_error_string, None), 'last_update': (self._get_last_update, None), }) # Build a parameter tree representation of the proxy target data self.data_param_tree = ParameterTree((self._get_data, None)) # Create an HTTP client instance and set up default request headers self.http_client = tornado.httpclient.HTTPClient() self.request_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', } self.remote_get() # init the data tree
def __init__(self): self.int_rw_param = 100 self.float_ro_param = 4.6593 self.int_ro_param = 1000 self.int_enum_param = 0 self.int_enum_param_allowed_values = [0, 1, 2, 3, 5, 8, 13] self.int_rw_param_metadata = { "min": 0, "max": 1000, "units": "arbitrary", "name": "intCallableRwParam", "description": "A callable integer RW parameter" } self.metadata_tree_dict = { 'name': 'Metadata Tree', 'description': 'A paramter tree to test metadata', 'floatRoParam': (self.floatRoParamGet, ), 'intRoParam': (self.intRoParamGet, { "units": "seconds" }), 'intCallableRwParam': (self.intCallableRwParamGet, self.intCallableRwParamSet, self.int_rw_param_metadata), 'intEnumParam': (0, { "allowed_values": self.int_enum_param_allowed_values }), 'valueParam': (24601, ), 'minNoMaxParam': (1, { 'min': 0 }) } self.metadata_tree = ParameterTree(self.metadata_tree_dict)
def __init__(self, pscu, sensor_idx): """Initialise the temperature data container for a sensor. This constructor initalises the data container for a temperature sensor, creating the parameter tree and associating it with a particular sensor on the PSCU. :param pscu: PSCU object to use to access the sensor :param sensor_idx: sensor index on PSCU """ self.param_tree = ParameterTree({ "temperature": (self.get_temperature, None), "temperature_volts": (self.get_temperature_volts, None), "setpoint": (self.get_set_point, None), "setpoint_volts": (self.get_set_point_volts, None), "tripped": (self.get_tripped, None), "trace": (self.get_trace, None), "disabled": (self.get_disabled, None), "name": (self.get_name, None), 'mode': (self.get_mode, None), }) self.pscu = pscu self.sensor_idx = sensor_idx
class ProxyTarget(object): """ Proxy adapter target class. This class implements a proxy target, its parameter tree and associated status information for use in the ProxyAdapter. """ def __init__(self, name, url, request_timeout): """ Initalise the ProxyTarget object. Sets up the default state of the target object, builds the appropriate parameter tree to be handled by the containing adapter and sets up the HTTP client for making requests to the target. """ self.name = name self.url = url self.request_timeout = request_timeout # Initialise default state self.status_code = 0 self.error_string = 'OK' self.last_update = 'unknown' self.data = {} self.counter = 0 # Build a parameter tree representation of the proxy target status self.status_param_tree = ParameterTree({ 'url': (self._get_url, None), 'status_code': (self._get_status_code, None), 'error': (self._get_error_string, None), 'last_update': (self._get_last_update, None), }) # Build a parameter tree representation of the proxy target data self.data_param_tree = ParameterTree((self._get_data, None)) # Create an HTTP client instance and set up default request headers self.http_client = tornado.httpclient.HTTPClient() self.request_headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', } self.remote_get() # init the data tree def update(self, request, path): """ Update the Proxy Target `ParameterTree` with data from the proxied adapter, after issuing a GET or a PUT request to it. It also updates the status code and error string if the HTTP request fails. """ try: # Request data to/from the target response = self.http_client.fetch(request) # Update status code and data accordingly self.status_code = response.code self.error_string = 'OK' response_body = tornado.escape.json_decode(response.body) except tornado.httpclient.HTTPError as http_err: # Handle HTTP errors, updating status information and reporting error self.status_code = http_err.code self.error_string = http_err.message logging.error( "Proxy target %s fetch failed: %d %s", self.name, self.status_code, self.error_string ) self.last_update = tornado.httputil.format_timestamp(time.time()) return except IOError as other_err: self.status_code = 502 self.error_string = str(other_err) logging.error( "Proxy target %s fetch failed: %d %s", self.name, self.status_code, self.error_string ) self.last_update = tornado.httputil.format_timestamp(time.time()) return data_ref = self.data # reference for modification if path: # if the path exists, we need to split it so we can navigate the data path_elems = path.split('/') if path_elems[-1] == '': # remove empty string caused by trailing slashes del path_elems[-1] for elem in path_elems[:-1]: # for each element, traverse down the data tree data_ref = data_ref[elem] for key in response_body: new_elem = response_body[key] data_ref[key] = new_elem logging.debug( "Proxy target %s fetch succeeded: %d %s", self.name, self.status_code, self.data_param_tree.get(path) ) # Update the timestamp of the last request in standard format self.last_update = tornado.httputil.format_timestamp(time.time()) def remote_get(self, path=''): """ Get data from the remote target. This method updates the local proxy target with new data by issuing a GET request to the target URL, and then updates the proxy target data and status information according to the response. """ # create request to PUT data, send to the target request = tornado.httpclient.HTTPRequest( url=self.url + path, method="GET", headers=self.request_headers, request_timeout=self.request_timeout ) self.update(request, path) def remote_set(self, path, data): """ Set data on the remote target. This method updates the local proxy target with new datat by issuing a PUT request to the target URL, and then updates the proxy target data and status information according to the response. """ # create request to PUT data, send to the target request = tornado.httpclient.HTTPRequest( url=self.url + path, method="PUT", body=data, headers=self.request_headers, request_timeout=self.request_timeout ) self.update(request, path) def _get_status_code(self): """ Get the target request status code. This internal method is used to retrieve the status code of the last target update request for use in the parameter tree. """ return self.status_code def _get_error_string(self): """ Get the target request error string. This internal method is used to retrieve the error string of the last target update request for use in the parameter tree. """ return self.error_string def _get_last_update(self): """ Get the target request last update timestamp. This internal method is used to retrieve the timestamp of the last target update request for use in the parameter tree. """ return self.last_update def _get_data(self): """ Get the target request data. This internal method is used to retrieve the target updated during last call to update(), for use in the parameter tree. """ return self.data def _get_url(self): return self.url
def initialise_proxy(self, proxy_target_cls): """ Initialise the proxy. This method initialises the proxy. The adapter options are parsed to determine the list of proxy targets and request timeout, then a proxy target of the specified class is created for each target. The data, metadata and status structures and parameter trees associated with each target are created. :param proxy_target_cls: proxy target class appropriate for the specific implementation """ # Set the HTTP request timeout if present in the options request_timeout = None if self.TIMEOUT_CONFIG_NAME in self.options: try: request_timeout = float(self.options[self.TIMEOUT_CONFIG_NAME]) logging.debug('Proxy adapter request timeout set to %f secs', request_timeout) except ValueError: logging.error( "Illegal timeout specified for proxy adapter: %s", self.options[self.TIMEOUT_CONFIG_NAME] ) # Parse the list of target-URL pairs from the options, instantiating a proxy target of the # specified type for each target specified. self.targets = [] if self.TARGET_CONFIG_NAME in self.options: for target_str in self.options[self.TARGET_CONFIG_NAME].split(','): try: (target, url) = target_str.split('=') self.targets.append( proxy_target_cls(target.strip(), url.strip(), request_timeout) ) except ValueError: logging.error("Illegal target specification for proxy adapter: %s", target_str.strip()) # Issue an error message if no targets were loaded if self.targets: logging.debug("Proxy adapter with {:d} targets loaded".format(len(self.targets))) else: logging.error("Failed to resolve targets for proxy adapter") # Build the parameter trees implemented by this adapter for the specified proxy targets status_dict = {} tree = {} meta_tree = {} for target in self.targets: status_dict[target.name] = target.status_param_tree tree[target.name] = target.data_param_tree meta_tree[target.name] = target.meta_param_tree # Create a parameter tree from the status data for the targets and insert into the # data and metadata structures self.status_tree = ParameterTree(status_dict) tree['status'] = self.status_tree meta_tree['status'] = self.status_tree.get("", True) # Create the data and metadata parameter trees self.param_tree = ParameterTree(tree) self.meta_param_tree = ParameterTree(meta_tree)
class ProxyAdapter(ApiAdapter): """ Proxy adapter class for ODIN server. This class implements a proxy adapter, allowing ODIN server to forward requests to other HTTP services. """ def __init__(self, **kwargs): """ Initialise the ProxyAdapter. This constructor initialises the adapter instance, parsing configuration options out of the keyword arguments it is passed. A ProxyTarget object is instantiated for each target specified in the options. :param kwargs: keyword arguments specifying options """ # Initialise base class super(ProxyAdapter, self).__init__(**kwargs) # Set the HTTP request timeout if present in the options request_timeout = None if TIMEOUT_CONFIG_NAME in self.options: try: request_timeout = float(self.options[TIMEOUT_CONFIG_NAME]) logging.debug('ProxyAdapter request timeout set to %f secs', request_timeout) except ValueError: logging.error( "Illegal timeout specified for ProxyAdapter: %s", self.options[TIMEOUT_CONFIG_NAME] ) # Parse the list of target-URL pairs from the options, instantiating a ProxyTarget # object for each target specified. self.targets = [] if TARGET_CONFIG_NAME in self.options: for target_str in self.options[TARGET_CONFIG_NAME].split(','): try: (target, url) = target_str.split('=') self.targets.append(ProxyTarget(target.strip(), url.strip(), request_timeout)) except ValueError: logging.error("Illegal target specification for ProxyAdapter: %s", target_str.strip()) # Issue an error message if no targets were loaded if self.targets: logging.debug("ProxyAdapter with {:d} targets loaded".format(len(self.targets))) else: logging.error("Failed to resolve targets for ProxyAdapter") # Construct the parameter tree returned by this adapter tree = {'status': {}} for target in self.targets: tree['status'][target.name] = target.status_param_tree tree[target.name] = target.data_param_tree self.param_tree = ParameterTree(tree) @request_types('application/json') @response_types('application/json', default='application/json') def get(self, path, request): """ Handle an HTTP GET request. This method handles an HTTP GET request, returning a JSON response. :param path: URI path of request :param request: HTTP request object :return: an ApiAdapterResponse object containing the appropriate response """ # Update the target specified in the path, or all targets if none specified if "/" in path: path_elem, target_path = path.split('/', 1) else: path_elem = path target_path = "" for target in self.targets: if path_elem == '' or path_elem == target.name: target.remote_get(target_path) # Build the response from the adapter parameter tree try: response = self.param_tree.get(path) status_code = 200 except ParameterTreeError as param_tree_err: response = {'error': str(param_tree_err)} status_code = 400 return ApiAdapterResponse(response, status_code=status_code) @request_types("application/json", "application/vnd.odin-native") @response_types('application/json', default='application/json') def put(self, path, request): """ Handle an HTTP PUT request. This method handles an HTTP PUT request, returning a JSON response. :param path: URI path of request :param request: HTTP request object :return: an ApiAdapterResponse object containing the appropriate response """ # Update the target specified in the path, or all targets if none specified try: json_decode(request.body) # ensure request body is JSON. Will throw a TypeError if not if "/" in path: path_elem, target_path = path.split('/', 1) else: path_elem = path target_path = "" for target in self.targets: if path_elem == '' or path_elem == target.name: target.remote_set(target_path, request.body) response = self.param_tree.get(path) status_code = 200 except ParameterTreeError as param_tree_err: response = {'error': str(param_tree_err)} status_code = 400 except (TypeError, ValueError) as type_val_err: response = {'error': 'Failed to decode PUT request body: {}'.format(str(type_val_err))} status_code = 415 return ApiAdapterResponse(response, status_code=status_code)
def __init__(self): """Initialise the SystemInfo object. This constructor initlialises the SystemInfo object, extracting various system-level parameters and storing them in a parameter tree to be accessible to clients. """ # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Extract platform information and store in parameter tree (system, node, release, version, _, processor) = platform.uname() platform_tree = ParameterTree({ 'name': 'platform', 'description': "Information about the underlying platform", 'system': (lambda: system, { "name": "system", "description": "operating system name", }), 'node': (lambda: node, { "name": "node", "description": "node (host) name", }), 'release': (lambda: release, { "name": "release", "description": "operating system release", }), 'version': (lambda: version, { "name": "version", "description": "operating system version", }), 'processor': (lambda: processor, { "name": "processor", "description": "processor (CPU) name", }), }) # Store all information in a parameter tree self.param_tree = ParameterTree({ 'name': 'system_info', 'description': 'Information about the system hosting this odin server instance', 'odin_version': (lambda: version_info['version'], { "name": "odin version", "description": "ODIN server version", }), 'tornado_version': (lambda: tornado.version, { "name": "tornado version", "description": "version of tornado used in this server", }), 'python_version': (lambda: platform.python_version(), { "name": "python version", "description": "version of python running this server", }), 'platform': platform_tree, 'server_uptime': (self.get_server_uptime, { "name": "server uptime", "description": "time since the ODIN server started", "units": "s", "display_precision": 2, }), })
class SystemInfo(with_metaclass(Singleton, object)): """SystemInfo - class that extracts and stores information about system-level parameters.""" # __metaclass__ = Singleton def __init__(self): """Initialise the SystemInfo object. This constructor initlialises the SystemInfo object, extracting various system-level parameters and storing them in a parameter tree to be accessible to clients. """ # Store initialisation time self.init_time = time.time() # Get package version information version_info = get_versions() # Extract platform information and store in parameter tree (system, node, release, version, _, processor) = platform.uname() platform_tree = ParameterTree({ 'name': 'platform', 'description': "Information about the underlying platform", 'system': (lambda: system, { "name": "system", "description": "operating system name", }), 'node': (lambda: node, { "name": "node", "description": "node (host) name", }), 'release': (lambda: release, { "name": "release", "description": "operating system release", }), 'version': (lambda: version, { "name": "version", "description": "operating system version", }), 'processor': (lambda: processor, { "name": "processor", "description": "processor (CPU) name", }), }) # Store all information in a parameter tree self.param_tree = ParameterTree({ 'name': 'system_info', 'description': 'Information about the system hosting this odin server instance', 'odin_version': (lambda: version_info['version'], { "name": "odin version", "description": "ODIN server version", }), 'tornado_version': (lambda: tornado.version, { "name": "tornado version", "description": "version of tornado used in this server", }), 'python_version': (lambda: platform.python_version(), { "name": "python version", "description": "version of python running this server", }), 'platform': platform_tree, 'server_uptime': (self.get_server_uptime, { "name": "server uptime", "description": "time since the ODIN server started", "units": "s", "display_precision": 2, }), }) def get_server_uptime(self): """Get the uptime for the ODIN server. This method returns the current uptime for the ODIN server. """ return time.time() - self.init_time def get(self, path, with_metadata=False): """Get the parameter tree. This method returns the parameter tree for use by clients via the SystemInfo adapter. :param path: path to retrieve from tree """ return self.param_tree.get(path, with_metadata)
class PSCUData(object): """Data container for a PSCU and associated quads. This class implements a data container and parameter tree of a PSCU, its assocated quad boxes and all sensors contained therein. A PSCUData object, asociated with a PSCU instance, forms the primary interface between, and data model for, the adapter and the underlying devices. """ def __init__(self, *args, **kwargs): """Initialise the PSCUData instance. This constructor initialises the PSCUData instance. If an existing PSCU instance is passed in as a keyword argument, that is used for accessing data, otherwise a new instance is created. :param args: positional arguments to be passed if creating a new PSCU :param kwargs: keyword arguments to be passed if creating a new PSCU, or if a pscu key is present, that is used as an existing PSCU object instance """ # If a PSCU has been passed in keyword arguments use that, otherwise create a new one if 'pscu' in kwargs: self.pscu = kwargs['pscu'] else: self.pscu = PSCU(*args, **kwargs) # Get the QuadData containers associated with the PSCU self.quad_data = [QuadData(quad=q) for q in self.pscu.quad] # Get the temperature and humidity containers associated with the PSCU self.temperature_data = [ TempData(self.pscu, i) for i in range(self.pscu.num_temperatures) ] self.humidity_data = [ HumidityData(self.pscu, i) for i in range(self.pscu.num_humidities) ] # Build the parameter tree of the PSCU self.param_tree = ParameterTree({ "quad": { "quads": [q.param_tree for q in self.quad_data], 'trace': (self.get_quad_traces, None), }, "temperature": { "sensors": [t.param_tree for t in self.temperature_data], "overall": (self.pscu.get_temperature_state, None), "latched": (self.pscu.get_temperature_latched, None), }, "humidity": { "sensors": [h.param_tree for h in self.humidity_data], "overall": (self.pscu.get_humidity_state, None), "latched": (self.pscu.get_humidity_latched, None), }, "fan": { "target": (self.pscu.get_fan_target, self.pscu.set_fan_target), "currentspeed_volts": (self.pscu.get_fan_speed_volts, None), "currentspeed": (self.pscu.get_fan_speed, None), "setpoint": (self.pscu.get_fan_set_point, None), "setpoint_volts": (self.pscu.get_fan_set_point_volts, None), "tripped": (self.pscu.get_fan_tripped, None), "overall": (self.pscu.get_fan_state, None), "latched": (self.pscu.get_fan_latched, None), "mode": (self.pscu.get_fan_mode, None), }, "pump": { "flow": (self.pscu.get_pump_flow, None), "flow_volts": (self.pscu.get_pump_flow_volts, None), "setpoint": (self.pscu.get_pump_set_point, None), "setpoint_volts": (self.pscu.get_pump_set_point_volts, None), "tripped": (self.pscu.get_pump_tripped, None), "overall": (self.pscu.get_pump_state, None), "latched": (self.pscu.get_pump_latched, None), "mode": (self.pscu.get_pump_mode, None), }, "trace": { "overall": (self.pscu.get_trace_state, None), "latched": (self.pscu.get_trace_latched, None), }, "position": (self.pscu.get_position, None), "position_volts": (self.pscu.get_position_volts, None), "overall": (self.pscu.get_health, None), "latched": (self.get_all_latched, None), "armed": (self.pscu.get_armed, self.pscu.set_armed), "allEnabled": (self.pscu.get_all_enabled, self.pscu.enable_all), "enableInterval": (self.pscu.get_enable_interval, None), "displayError": (self.pscu.get_display_error, None), }) def get(self, path): """Get parameters from the underlying parameter tree. This method simply wraps underlying ParameterTree method so that an exceptions can be re-raised with an appropriate PSCUDataError. :param path: path of parameter tree to get :returns: parameter tree at that path as a dictionary """ try: return self.param_tree.get(path) except ParameterTreeError as e: raise PSCUDataError(e) def set(self, path, data): """Set parameters in underlying parameter tree. This method simply wraps underlying ParameterTree method so that an exceptions can be re-raised with an appropriate PSCUDataError. :param path: path of parameter tree to set values for :param data: dictionary of new data values to set in the parameter tree """ try: self.param_tree.set(path, data) except ParameterTreeError as e: raise PSCUDataError(e) def get_all_latched(self): """Return the global latch status of the PSCU. This method returns the global latch status for the PSCU, which is the logical AND of PSCU latch channels :returns: global latch status as bool """ return all(self.pscu.get_all_latched()) def get_quad_traces(self): """Return the trace status for the quads in the PSCU. This method returns a dictionary of the quad trace status values for the PSCU. :returns: dictionary of the quad trace status """ return {str(q): self.pscu.get_quad_trace(q) for q in range(self.pscu.num_quads)}
def __init__(self, *args, **kwargs): """Initialise the PSCUData instance. This constructor initialises the PSCUData instance. If an existing PSCU instance is passed in as a keyword argument, that is used for accessing data, otherwise a new instance is created. :param args: positional arguments to be passed if creating a new PSCU :param kwargs: keyword arguments to be passed if creating a new PSCU, or if a pscu key is present, that is used as an existing PSCU object instance """ # If a PSCU has been passed in keyword arguments use that, otherwise create a new one if 'pscu' in kwargs: self.pscu = kwargs['pscu'] else: self.pscu = PSCU(*args, **kwargs) # Get the QuadData containers associated with the PSCU self.quad_data = [QuadData(quad=q) for q in self.pscu.quad] # Get the temperature and humidity containers associated with the PSCU self.temperature_data = [ TempData(self.pscu, i) for i in range(self.pscu.num_temperatures) ] self.humidity_data = [ HumidityData(self.pscu, i) for i in range(self.pscu.num_humidities) ] # Build the parameter tree of the PSCU self.param_tree = ParameterTree({ "quad": { "quads": [q.param_tree for q in self.quad_data], 'trace': (self.get_quad_traces, None), }, "temperature": { "sensors": [t.param_tree for t in self.temperature_data], "overall": (self.pscu.get_temperature_state, None), "latched": (self.pscu.get_temperature_latched, None), }, "humidity": { "sensors": [h.param_tree for h in self.humidity_data], "overall": (self.pscu.get_humidity_state, None), "latched": (self.pscu.get_humidity_latched, None), }, "fan": { "target": (self.pscu.get_fan_target, self.pscu.set_fan_target), "currentspeed_volts": (self.pscu.get_fan_speed_volts, None), "currentspeed": (self.pscu.get_fan_speed, None), "setpoint": (self.pscu.get_fan_set_point, None), "setpoint_volts": (self.pscu.get_fan_set_point_volts, None), "tripped": (self.pscu.get_fan_tripped, None), "overall": (self.pscu.get_fan_state, None), "latched": (self.pscu.get_fan_latched, None), "mode": (self.pscu.get_fan_mode, None), }, "pump": { "flow": (self.pscu.get_pump_flow, None), "flow_volts": (self.pscu.get_pump_flow_volts, None), "setpoint": (self.pscu.get_pump_set_point, None), "setpoint_volts": (self.pscu.get_pump_set_point_volts, None), "tripped": (self.pscu.get_pump_tripped, None), "overall": (self.pscu.get_pump_state, None), "latched": (self.pscu.get_pump_latched, None), "mode": (self.pscu.get_pump_mode, None), }, "trace": { "overall": (self.pscu.get_trace_state, None), "latched": (self.pscu.get_trace_latched, None), }, "position": (self.pscu.get_position, None), "position_volts": (self.pscu.get_position_volts, None), "overall": (self.pscu.get_health, None), "latched": (self.get_all_latched, None), "armed": (self.pscu.get_armed, self.pscu.set_armed), "allEnabled": (self.pscu.get_all_enabled, self.pscu.enable_all), "enableInterval": (self.pscu.get_enable_interval, None), "displayError": (self.pscu.get_display_error, None), })