1
"""
2
Modules are the basic building blocks of Pyrpl.
3

4
The internal structure of the FPGA is made of individual modules, each
5
performing a well defined task. Each of these FPGA modules are represented
6
in python by a :obj:`HardwareModule`.
7

8
Higher-level operations, for instance those that need a coordinated
9
operation of several HardwareModules is performed by a SoftwareModule,
10
defined in a class derived from :obj:`Module`.
11

12
Thus, all modules (both :obj:`HardwareModule` and Software modules inherit
13
from :obj:`Module` which gives them basic capabilities such as displaying
14
their attributes in the GUI having their state load and saved in the config
15
file.
16
"""
17

18 3
from .attributes import BaseAttribute, ModuleAttribute
19 3
from .widgets.module_widgets import ModuleWidget
20 3
from .curvedb import CurveDB
21 3
from .pyrpl_utils import unique_list, DuplicateFilter
22

23 3
import logging
24 3
import numpy as np
25 3
from six import with_metaclass
26 3
from collections import OrderedDict
27 3
from qtpy import QtCore
28

29

30 3
class SignalLauncher(QtCore.QObject):
31
    """
32
    Object that is used to handle signal the emission for a :obj:`Module`.
33

34
    A QObject that is connected to the widgets to update their value when
35
    attributes of a module change. Any timers needed to implement the module
36
    functionality shoud be implemented here as well.
37
    """
38 3
    update_attribute_by_name = QtCore.Signal(str, list)
39
    # The name of the property that has changed, the list is [new_value],
40
    # the new_value of the attribute
41 3
    change_options = QtCore.Signal(str, list) # name of the
42
    # SelectProperty,  list of new options
43 3
    refresh_filter_options = QtCore.Signal(str) # name of the
44
    # FilterProperty,  new options are contained in self.valid_frequencies()
45 3
    change_ownership = QtCore.Signal() # The owner of the module  has
46
    # changed
47

48 3
    def __init__(self, module):
49 3
        super(SignalLauncher, self).__init__()
50 3
        self.module = module
51

52 3
    def emit_signal_by_name(self, name, *args, **kwds):
53
        """Emits signal "name" with the specfified args and kwds."""
54 0
        signal = getattr(self, name)
55 0
        signal.emit(*args, **kwds)
56

57 3
    def connect_widget(self, widget):
58
        """
59
        Establishes all connections between the module and the widget by name.
60
        """
61
        #self.update_attribute_by_name.connect(widget.update_attribute_by_name)
62 3
        for key in dir(self.__class__):
63 3
            val = getattr(self, key)
64 3
            if isinstance(val, QtCore.pyqtBoundSignal) and hasattr(widget,
65
                                                                   key):
66 3
                val.connect(getattr(widget, key))
67

68 3
    def _clear(self):
69
        """ Destroys the object by disconnecting all signals and by killing all timers"""
70 0
        for key in dir(self.__class__):
71 0
            val = getattr(self, key)
72 0
            if isinstance(val, QtCore.pyqtBoundSignal):
73 0
                try:
74 0
                    val.disconnect()
75 0
                except TypeError:  # occurs if signal is not connected to anything
76 0
                    pass
77

78

79 3
class ModuleMetaClass(type):
80
    """
81
    Generate Module classes with two features:
82
    - __new__ lets attributes know what name they are referred to in the
83
    class that contains them.
84
    - __new__ also lists all the submodules. This info will be used when
85
    instantiating submodules at module instanciation time.
86
    - __init__ auto-generates the function setup() and its docstring """
87 3
    def __init__(self, classname, bases, classDict):
88
        """
89
        Magic to retrieve the name of the attributes in the attributes
90
        themselves.
91
        see http://code.activestate.com/recipes/577426-auto-named-decriptors/
92
        Iterate through the new class' __dict__ and update the .name of all
93
        recognised BaseAttribute.
94

95
        + list all submodules attributes
96

97
        formerly __init__
98
        1. Takes care of adding all submodules attributes to the list
99
        self._module_attributes
100

101
        2. Takes care of creating 'setup(**kwds)' function of the module.
102
        The setup function executes set_attributes(**kwds) and then _setup().
103

104
        We cannot use normal inheritance because we want a customized
105
        docstring for each module. The docstring is created here by
106
        concatenating the module's _setup docstring and individual
107
        setup_attribute docstrings.
108
        """
109 3
        if classname == 'ModuleContainer':
110 0
            pass
111

112
        # 0. make all attributes aware of their name in the class containing them
113 3
        for name, attr in self.__dict__.items():
114 3
            if isinstance(attr, BaseAttribute):
115 3
                attr.name = name
116
        # 1a. prepare _setup_attributes etc.
117 3
        _setup_attributes, _gui_attributes, _module_attributes = [], [], []
118

119 3
        for base in reversed(bases):  # append all base class _setup_attributes
120 3
            try: _setup_attributes += base._setup_attributes
121 3
            except AttributeError: pass
122 3
            try: _gui_attributes += base._gui_attributes
123 3
            except AttributeError: pass
124 3
            try: _module_attributes += base._module_attributes
125 3
            except AttributeError: pass
126 3
        _setup_attributes += self._setup_attributes
127 3
        _gui_attributes += self._gui_attributes
128
        # 1b. make a list of _module_attributes and add _module_attributes to _setup_attributes
129 3
        for name, attr in self.__dict__.items():
130 3
            if isinstance(attr, ModuleAttribute):
131 3
                _module_attributes.append(name)
132 3
        self._module_attributes = unique_list(_module_attributes)
133
        # 1c. add _module_attributes to _setup_attributes if the submodule has _setup_attributes
134 3
        for name in self._module_attributes:
135 3
            attr = getattr(self, name)
136 1
            if True:  #len(attr.module_cls._setup_attributes) > 0:
137 3
                _setup_attributes.append(name)
138
        #1d. Set the unique list of _setup_attributes
139 3
        self._setup_attributes = unique_list(_setup_attributes)
140 3
        self._gui_attributes = unique_list(_gui_attributes)
141
        # 2. create setup(**kwds)
142 3
        if "setup" not in classDict:
143
            # a. generate a setup function
144 3
            def setup(self, **kwds):
145 3
                self._setup_ongoing = True
146 3
                try:
147
                    # user can redefine any setup_attribute through kwds
148 3
                    for key in self._setup_attributes:
149 3
                        if key in kwds:
150 0
                            value = kwds.pop(key)
151 0
                            setattr(self, key, value)
152 3
                    if len(kwds) > 0:
153 0
                        self._logger.warning(
154
                            "Trying to load attribute %s of module %s that "
155
                            "are invalid setup_attributes.",
156
                            sorted(kwds.keys())[0], self.name)
157 3
                    if hasattr(self, '_setup'):
158 3
                        self._setup()
159
                finally:
160 3
                    self._setup_ongoing = False
161
            # b. place the new setup function in the module class
162 3
            self.setup = setup
163
        # 3. if setup has no docstring, then make one
164 3
        self.make_setup_docstring(classDict)
165
        # 4. make the new class
166
        #return super(ModuleMetaClass, cls).__new__(cls, classname, bases, classDict)
167

168
    #@classmethod
169 3
    def make_setup_docstring(self, classDict):
170
        """
171
        Returns a docstring for the function 'setup' that is composed of:
172
          - the '_setup' docstring
173
          - the list of all setup_attributes docstrings
174
        """
175
        # get initial docstring (python 2 and python 3 syntax)
176 3
        try: doc = self._setup.__doc__ + '\n'
177 3
        except:
178 3
            try: doc = self._setup.__func__.__doc__ + '\n'
179 3
            except: doc = ""
180 3
        doc += "attributes\n=========="
181 3
        for attr_name in self._setup_attributes:
182 3
            attr = getattr(self, attr_name)
183 3
            doc += "\n  " + attr_name + ": " + attr.__doc__
184 3
        setup = self.setup
185
        # docstring syntax differs between python versions. Python 2:
186 3
        if hasattr(setup, "__func__"):
187 1
            setup.__func__.__doc__ = doc
188
        # ... python 3
189 2
        elif hasattr(setup, '__doc__'):
190 2
            setup.__doc__ = doc
191

192

193 3
class DoSetup(object):
194
    """
195
    A context manager that allows to nicely write Module setup functions.
196

197
    Usage example in :py:meth:`Module._setup()`::
198

199
        def _setup(self):
200
            # _setup_ongoing is False by default
201
            assert self._setup_ongoing == False
202
            with self.do_setup:
203
                # now _setup_ongoing is True
204
                assert self._setup_ongoing == True
205
                # do stuff that might fail
206
                raise BaseException()
207
            # even if _setup fails, _setup_ongoing is False afterwards or in
208
            # the next call to _setup()
209
            assert self._setup_ongoing == False
210
    """
211 3
    def __init__(self, parent):
212 3
        self.parent = parent
213

214 3
    def __enter__(self):
215 0
        self.parent._setup_ongoing = True
216

217 3
    def __exit__(self, exc_type, exc_val, exc_tb):
218 0
        self.parent._setup_ongoing = False
219 0
        if exc_type is not None:
220 0
            self.parent._logger.warning("Exception %s was raised while "
221
                                        "_setup_ongoing was True: %s, %s",
222
                                        exc_type, exc_val, exc_tb)
223

224

225 3
class Module(with_metaclass(ModuleMetaClass, object)):
226
    # The Syntax for defining a metaclass changed from Python 2 to 3.
227
    # with_metaclass is compatible with both versions and roughly does this:
228
    # def with_metaclass(meta, *bases):
229
    #     """Create a base class with a metaclass."""
230
    #     return meta("NewBase", bases, {})
231
    # Specifically, ModuleMetaClass ensures that attributes have automatically
232
    # their internal name set properly upon module creation.
233
    """
234
    A module is a component of pyrpl doing a specific task.
235

236
    Module is the base class for instruments such as the
237
    Scope/Lockbox/NetworkAnalyzer. A module can have a widget to build a
238
    graphical user interface on top of it.
239
    It is composed of attributes (see attributes.py) whose values represent
240
    the current state of the module (more precisely, the state is defined
241
    by the value of all attributes in _setup_attributes)
242
    The module can be slaved or freed by a user or another module. When the
243
    module is freed, it goes back to the state immediately before being
244
    slaved. To make sure the module is freed, use the syntax::
245

246
        with pyrpl.mod_mag.pop('owner') as mod:
247
            mod.do_something()
248
            mod.do_something_else()
249

250
    Attributes:
251
        `get_setup_attributes()`: returns a dict with the current values of
252
            the setup attributes
253
        ``set_setup_attributes(**kwds)``: sets the provided setup_attributes
254
            (setup is not called)
255
        `save_state(name)`: saves the current 'state' (using
256
            get_setup_attribute) into the config file
257
        `load_state(name)`: loads the state 'name' from the config file (setup
258
            is not called by default)
259
        `erase_state(name)`: erases state 'name' from config file
260
        `create_widget()`: returns a widget according to widget_class
261
        ``setup(**kwds)``: first, performs :code:`set_setup_attributes(**kwds)`,
262
            then calls _setup() to set the module ready for acquisition. This
263
            method is automatically created by ModuleMetaClass and it combines the
264
            docstring of individual setup_attributes with the docstring of _setup()
265
        `free()`: sets the module owner to None, and brings the module back the
266
            state before it was slaved equivalent to module.owner = None)
267
        `get_yml(state=None)`: get the yml code representing the state "state'
268
            or the current state if state is None
269
        `set_yml(yml_content, state=None)`: sets the state "state" with the
270
            content of yml_content. If state is None, the state is directly loaded
271
            into the module.
272
        `name`: attributed based on name at instance creation
273
            (also used as a section key in the config file)
274
        `states (list)`: the list of states available in the config file
275
        `owner (string)`: a module can be owned (reserved) by a user or another
276
            module. The module is free if and only if owner is None
277
        `pyrpl` (:obj:`Pyrpl`): recursively looks through parent modules until it
278
            reaches the Pyrpl instance
279

280
    Class attributes to be implemented in derived class:
281

282
    - all individual attributes (instances of BaseAttribute)
283
    - _setup_attributes: attribute names that are touched by setup(**kwds)/
284
      saved/restored upon module creation
285
    - _gui_attributes: attribute names to be displayed by the widget
286
    - _callback_attributes: attribute_names that triggers a callback when
287
      their value is changed in the base class, _callback just calls setup()
288
    - _widget_class: class of the widget to use to represent the module in
289
      the gui(a child of ModuleWidget)
290

291
    Methods to implement in derived class:
292

293
    - _setup(): sets the module ready for acquisition/output with the
294
      current attribute's values. The metaclass of the module autogenerates a
295
      function like this::
296

297
          def setup(self, **kwds):
298
              \"\"\"
299
              _ docstring is the result of the following pseudocode: _
300
              print(DOCSTRING_OF_FUNCTION("_setup"))
301
              for attribute in self.setup_attributes:
302
                  print(DOCSTRING_OF_ATTRIBUTE(attribute))
303
              \"\"\"
304
              self.set_setup_attributes(kwds)
305
              return self._setup()
306

307
    - _ownership_changed(old, new): this function is called when the module
308
      owner changes it can be used to stop the acquisition for instance.
309
    """
310

311
    # Change this to provide a custom graphical class
312 3
    _widget_class = ModuleWidget
313

314
    # the class for the SignalLauncher to be used
315
    # a QOBject used to communicate with the widget
316 3
    _signal_launcher = SignalLauncher
317

318
    # attributes listed here will be saved in the config file everytime they
319
    # are updated.
320 3
    _setup_attributes = []
321

322
    # class inheriting from ModuleWidget can
323
    # automatically generate gui from a list of attributes
324 3
    _gui_attributes = []
325

326
    # This flag is used to desactivate callback during setup
327 3
    _setup_ongoing = False
328

329
    # internal memory for owner of the module (to avoid conflicts)
330 3
    _owner = None
331

332
    # name of the module, metaclass automatically assigns one per instance
333 3
    name = None
334

335 3
    def __init__(self, parent, name=None):
336
        """
337
        Creates a module with given name. If name is None, cls.name is
338
        assigned by the metaclass.
339

340
        Parent is either
341
          - a pyrpl instance: config file entry is in
342
            (self.__class__.name + 's').(self.name)
343
          - or another SoftwareModule: config file entry is in
344
            (parent_entry).(self.__class__.name + 's').(self.name)
345
        """
346 3
        if name is not None:
347 3
            self.name = name
348 3
        self.do_setup = DoSetup(self)  # ContextManager for _setup_ongoing
349 3
        self._flag_autosave_active = True # I would have prefered to use
350
        # __autosave_active, but this gets automatically name mangled:
351
        # see http://stackoverflow.com/questions/1301346/what-is-the-meaning-of-a-single-and-a-double-underscore-before-an-object-name
352 3
        self._logger = logging.getLogger(name=__name__)
353 3
        self._logger.addFilter(DuplicateFilter())
354
        # create the signal launcher object from its class
355 3
        self._signal_launcher = self._signal_launcher(self)
356 3
        self.parent = parent
357
        # disable autosave during initialization
358 3
        self._autosave_active = False
359
        # instantiate modules associated with _module_attribute by calling their getter
360 3
        for submodule in self._module_attributes:
361 3
            getattr(self, submodule)
362
        # custom module initialization hook
363
        # self._init_module()
364
        # enable autosave and load last state from config file
365 3
        self._autosave_active = True
366
        # Only top level modules should call _load_setup_attributes() since
367
        # this call propagates through all child modules
368
        ##if not isinstance(self.parent, Module):
369
        ##    # attributes are loaded but _setup() is not called
370
        ##   self._load_setup_attributes()
371

372 3
    def _init_module(self):
373
        """
374
        To implement in child class if needed.
375
        """
376 0
        self._logger.warning("Function _init_module is obsolete and will be "
377
                             "removed soon. Please migrate the corresponding "
378
                             "code to __init__.")
379

380 3
    @property
381
    def _autosave_active(self):
382
        """
383
        :return: If an ancestor of the current module is NOT autosaving, then
384
         the current module is not saving either.
385
        """
386 3
        try:
387 3
            parent_autosave_active = self.parent._autosave_active
388 3
        except AttributeError:  # some parents do not implement the autosave flag
389 3
            parent_autosave_active = True
390 3
        return self._flag_autosave_active and parent_autosave_active
391

392 3
    @_autosave_active.setter
393
    def _autosave_active(self, val):
394
        """
395
        Only takes effect when all ancestor are autosaving
396
        :param val:
397
        :return:
398
        """
399 3
        self._flag_autosave_active = val
400

401 3
    @property
402
    def _modules(self):
403 0
        return dict([(key, getattr(self, key)) for key in
404
                     self._module_attributes])
405

406 3
    @property
407
    def pyrpl(self):
408
        """
409
        Recursively looks through patent modules untill pyrpl instance is
410
        reached.
411
        """
412 0
        from .pyrpl import Pyrpl
413 0
        parent = self.parent
414 0
        while (not isinstance(parent, Pyrpl)):
415 0
            parent = parent.parent
416 0
        return parent
417

418 3
    def get_setup_attributes(self):
419
        """
420
        Returns a dict with the current values of the setup attributes.
421

422
        Recursively calls get_setup_attributes for sub_modules and assembles
423
        a hierarchical dictionary.
424

425
        Returns:
426
            dict: contains setup_attributes and their current values.
427
        """
428 0
        self._logger.warning("get_setup_attributes is deprecated. Use property setup_attributes instead. ")
429 0
        return self.setup_attributes
430

431 3
    @property
432
    def setup_attributes(self):
433
        """
434
        :return: a dict with the current values of the setup attributes.
435
        Recursively collects setup_attributes for sub_modules.
436
        """
437 0
        kwds = OrderedDict()
438 0
        for attr in self._setup_attributes:
439 0
            val = getattr(self, attr)
440 0
            if attr in self._modules:
441 0
                val = val.setup_attributes
442 0
            kwds[attr] = val
443 0
        return kwds
444

445 3
    def set_setup_attributes(self, **kwds):
446
        """
447
        Sets the values of the setup attributes. Without calling any callbacks
448
        """
449 0
        self._logger.warning("set_setup_attributes is deprecated. Use property setup_attributes instead. ")
450 0
        self.setup_attributes = kwds
451

452 3
    @setup_attributes.setter
453
    def setup_attributes(self, kwds):
454
        """
455
        Sets the values of the setup attributes.
456
        """
457 0
        self.setup(**kwds)
458

459 3
    def _load_setup_attributes(self):
460
        """
461
         Load and sets all setup attributes from config file
462
        """
463
        # self.c = None switches off loading states (e.g. for ModuleManagers).
464
        # First part of the if avoids creating an empty branch in the
465
        # config file at the call of this function at startup.
466 0
        if (self.name in self.parent.c) and (self.c is not None):
467
            # pick those elements of the config state that are setup_attributes
468 0
            dic = {k: v for k, v in self.c._data.items() if k in self._setup_attributes}
469
            # set those elements
470 0
            self.setup_attributes = dic
471

472 3
    @property
473
    def c(self):
474
        """
475
        Returns a MemoryBranch object used for storing data in the configuration file.
476

477
        The branch corresponding to the module is a subbranch of the parent module's branch with the name of the module.
478
        """
479 0
        return self.parent.c._get_or_create(self.name)
480

481 3
    @property
482
    def _states(self):
483
        """
484
        Returns the config file branch corresponding to the saved states of the module.
485
        """
486 0
        return self.c._root._get_or_create(self.name + "_states")
487

488 3
    @property
489
    def states(self):
490
        """
491
        Returns the names of all saved states of the module.
492
        """
493
        # the if avoids creating an empty states section for all modules
494 0
        if (self.name + "_states") in self.parent.c._root._data:
495 0
            return list(self._states._keys())
496
        else:
497 0
            return []
498

499 3
    def save_state(self, name=None):
500
        """
501
        Saves the current state under the name "name" in the config file. If
502
        state_section is left unchanged, uses the normal
503
        class_section.states convention.
504
        """
505 0
        if name is None:
506 0
            self.setup_attributes = self.setup_attributes
507
        else:
508 0
            self._states[name] = self.setup_attributes
509

510 3
    def load_state(self, name=None):
511
        """
512
        Loads the state with name "name" from the config file. If
513
        state_branch is left unchanged, uses the normal
514
        class_section.states convention.
515
        """
516 0
        if name is None:
517 0
            self.setup_attributes = self.c._data
518
        else:
519 0
            self.setup_attributes = self._states[name]._data
520

521 3
    def erase_state(self, name):
522
        """
523
        Removes the state "name' from the config file
524
        :param name: name of the state to erase
525
        :return: None
526
        """
527 0
        self._states[name]._erase()
528

529 3
    def get_yml(self, state=None):
530
        """
531
        :param state: The name of the state to inspect. If state is None-->
532
        then, use the current instrument state.
533
        :return: a string containing the yml code
534
        """
535 0
        if state is None:
536 0
            return self.c._get_yml()
537
        else:
538 0
            return self._states[state]._get_yml()
539

540 3
    def set_yml(self, yml_content, state=None):
541
        """
542
        :param yml_content: some yml code to encode the module content.
543
        :param state: The name of the state to set. If state is None-->
544
        then, use the current instrument state and reloads it immediately
545
        :return: None
546
        """
547 0
        if state is None:
548 0
            self.c._set_yml(yml_content)
549 0
            self._load_setup_attributes()
550
        else:
551 0
            self._states._get_or_create(state)._set_yml(yml_content)
552

553 3
    def _save_curve(self, x_values, y_values, **attributes):
554
        """
555
        Saves a curve in some database system.
556
        To change the database system, overwrite this function
557
        or patch Module.curvedb if the interface is identical.
558

559
        :param  x_values: numpy array with x values
560
        :param  y_values: numpy array with y values
561
        :param  attributes: extra curve parameters (such as relevant module
562
        settings)
563
        """
564 0
        curve = CurveDB.create(x_values,
565
                               y_values,
566
                               **attributes)
567 0
        return curve
568

569 3
    def free(self):
570
        """
571
        Change ownership to None
572
        """
573 0
        self.owner = None
574

575 3
    def _setup(self):
576
        """
577
        Sets the module up for acquisition with the current setup attribute
578
        values.
579
        """
580 0
        pass
581

582
    # def help(self, register=''):
583
    #     """returns the docstring of the specified register name
584
    #        if register is an empty string, all available docstrings are
585
    #        returned"""
586
    #     if register:
587
    #         string = type(self).__dict__[register].__doc__
588
    #         return string
589
    #     else:
590
    #         string = ""
591
    #         for key in type(self).__dict__.keys():
592
    #             if isinstance(type(self).__dict__[key], BaseAttribute):
593
    #                 docstring = self.help(key)
594
    #                 # mute internal registers
595
    #                 if not docstring.startswith('_'):
596
    #                     string += key + ": " + docstring + '\r\n\r\n'
597
    #         return string
598 3
    def help(self, register=''):
599 0
        return "Please refer to the docstring of the function setup() or " \
600
               "to the manual for further help! "
601

602 3
    def _create_widget(self):
603
        """
604
        Creates the widget specified in widget_class.
605
        """
606 0
        if self._widget_class is None:
607 0
            self._logger.warning("Module %s of type %s is trying to create a widget, but no widget_class is defined!",
608
                                 self.name, type(self))
609 0
            return None
610 0
        try:
611 0
            widget = self._widget_class(self.name, self)
612
        finally:
613 0
            pass
614 0
        self._module_widget = widget # For debugging purpose only (all
615
        # communications to the widget should happen via signals)
616 0
        return widget
617

618 3
    @property
619
    def owner(self):
620 0
        return self._owner
621

622 3
    @owner.setter
623
    def owner(self, val):
624
        """
625
        Changing module ownership automagically:
626
         - changes the visibility of the module_widget in the gui
627
         - re-setups the module with the module attributes in the config-file
628
           if new ownership is None
629
        """
630 0
        old = self.owner
631 0
        self._owner = val
632 0
        if val is None:
633 0
            self._autosave_active = True
634
        else:
635
            # deactivate autosave for slave modules
636 0
            self._autosave_active = False
637 0
        self._ownership_changed(old, val)
638 0
        if val is None:
639 0
            self._load_setup_attributes()
640
            # self.set_setup_attributes(**self.c._dict)
641
            # using the same dict will create a reference (&id) in the
642
            # config file for submodules --> That is probably a bug that
643
            # could be solved by making a copy of the dict somewhere in
644
            # memory.py, but on the other hand we are not supposed to use
645
            # anything but the public API of memory.py
646 0
        self._signal_launcher.change_ownership.emit()
647

648 3
    def _ownership_changed(self, old, new):
649 0
        pass
650

651 3
    def __enter__(self):
652
        """
653
        This function is executed in the context manager construct with
654
        ... as ... :
655
        """
656 0
        return self
657

658 3
    def __exit__(self, type, val, traceback):
659
        """
660
        To make sure the module will be freed afterwards, use the context
661
         manager construct:
662
        with pyrpl.module_manager.pop('owner') as mod:
663
            mod.do_something()
664
        # module automatically freed at this point
665

666
        The free operation is performed in this function
667
        see http://stackoverflow.com/questions/1369526/what-is-the-python-keyword-with-used-for
668
        """
669 0
        self.owner = None
670

671 3
    def _clear(self):
672
        """
673
        Kill timers and free resources for this module and all submodules.
674
        """
675 0
        self._signal_launcher._clear()
676 0
        for sub in self._modules:
677 0
            getattr(self, sub)._clear()
678

679

680 3
class HardwareModule(Module):
681
    """
682
    Module that directly maps a FPGA module. In addition to BaseModule's
683
    requirements, HardwareModule classes must have the following class
684
    attributes:
685

686
    - addr_base (int): the base address of the module, such as 0x40300000
687
    """
688

689 3
    parent = None  # parent will be redpitaya instance
690

691 3
    def __init__(self, parent, name=None):
692
        """ Creates the prototype of a RedPitaya Module interface
693

694
        if no name provided, will use cls.name
695
        """
696 0
        self._client = parent.client
697 0
        self._addr_base = self.addr_base
698 0
        self._rp = parent
699 0
        super(HardwareModule, self).__init__(parent, name=name)
700 0
        self.__doc__ = "Available registers: \r\n\r\n" + self.help()
701

702 3
    def _ownership_changed(self, old, new):
703
        """
704
        This hook is there to make sure any ongoing measurement is stopped when
705
        the module gets slaved
706

707
        old: name of old owner (eventually None)
708
        new: name of new owner (eventually None)
709
        """
710 0
        pass
711

712 3
    @property
713
    def _frequency_correction(self):
714
        """
715
        factor to manually compensate 125 MHz oscillator frequency error
716
        real_frequency = 125 MHz * _frequency_correction
717
        """
718 0
        try:
719 0
            return self._rp.frequency_correction
720 0
        except AttributeError:
721 0
            self._logger.warning("Warning: Parent of %s has no attribute "
722
                                 "'frequency_correction'. ", self.name)
723 0
            return 1.0
724

725 3
    def _reads(self, addr, length):
726 0
        return self._client.reads(self._addr_base + addr, length)
727

728 3
    def _writes(self, addr, values):
729 0
        self._client.writes(self._addr_base + addr, values)
730

731 3
    def _read(self, addr):
732 0
        return int(self._reads(addr, 1)[0])
733

734 3
    def _write(self, addr, value):
735 0
        self._writes(addr, [int(value)])
736

737 3
    def _to_pyint(self, v, bitlength=14):
738 0
        v = v & (2 ** bitlength - 1)
739 0
        if v >> (bitlength - 1):
740 0
            v = v - 2 ** bitlength
741 0
        return int(v)
742

743 3
    def _from_pyint(self, v, bitlength=14):
744 0
        v = int(v)
745 0
        if v < 0:
746 0
            v = v + 2 ** bitlength
747 0
        v = (v & (2 ** bitlength - 1))
748 0
        return np.uint32(v)
749

750

751 3
class SignalModule(Module):
752
    """ any module that can be passed as an input to another module"""
753 3
    def signal(self):
754 0
        return self.name

Read our documentation on viewing source code .

Loading