entity.py 10.5 KB
Newer Older
1
2
3
"""
  Copyright (C) 2010 CNRS

4
  Author: Florent Lamiraux, Nicolas Mansard
5
"""
6
7
from __future__ import print_function

Guilhem Saurel's avatar
Guilhem Saurel committed
8
import types
Guilhem Saurel's avatar
Guilhem Saurel committed
9
10
from enum import Enum

Guilhem Saurel's avatar
Guilhem Saurel committed
11
12
from . import signal_base, wrap
from .attrpath import setattrpath
13

Florent Lamiraux's avatar
Florent Lamiraux committed
14
if 'display' not in globals().keys():
Guilhem Saurel's avatar
Guilhem Saurel committed
15

Florent Lamiraux's avatar
Florent Lamiraux committed
16
17
18
    def display(s):
        print(s)

Guilhem Saurel's avatar
Guilhem Saurel committed
19

20
# --- FACTORY ------------------------------------------------------------------
21

Guilhem Saurel's avatar
Guilhem Saurel committed
22

23
24
25
26
27
class PyEntityFactoryClass(type):
    """
    The class build dynamically a new class type, and return the reference
    on the class-type object. The class type is not added to any context.
    """
Guilhem Saurel's avatar
Guilhem Saurel committed
28
29
    def __new__(factory, className, bases=(), dict={}):
        if len(bases) == 0:
30
            # Initialize a basic Entity class
Guilhem Saurel's avatar
Guilhem Saurel committed
31
            EntityClass = type.__new__(factory, className, (Entity, ), dict)
32
33
34
35
36
37
            EntityClass.className = className
            EntityClass.__init__ = Entity.initEntity
        else:
            # Initialize a heritated class
            EntityClass = type.__new__(factory, className, bases, dict)
            for c in bases:
Guilhem Saurel's avatar
Guilhem Saurel committed
38
                if issubclass(c, Entity):
39
40
                    EntityClass.className = c.className
                    break
41
42
43
        EntityClass.commandCreated = False
        return EntityClass

Guilhem Saurel's avatar
Guilhem Saurel committed
44
45

def PyEntityFactory(className, context):
46
47
48
49
    """
    Build a new class type by calling the factory, and add it
    to the given context.
    """
Guilhem Saurel's avatar
Guilhem Saurel committed
50
51
    EntityClass = PyEntityFactoryClass(className)
    context[className] = EntityClass
52
53
    return EntityClass

Guilhem Saurel's avatar
Guilhem Saurel committed
54

55
def updateEntityClasses(dictionary):
56
    """
57
58
59
    For all c++entity types that are not in the pyentity class list
    (entityClassNameList) run the factory and store the new type in the given
    context (dictionary).
60
    """
61
    cxx_entityList = wrap.factory_get_entity_class_list()
Guilhem Saurel's avatar
Guilhem Saurel committed
62
    for e in filter(lambda x: x not in Entity.entityClassNameList, cxx_entityList):
63
        # Store new class in dictionary with class name
Guilhem Saurel's avatar
Guilhem Saurel committed
64
        PyEntityFactory(e, dictionary)
65
        # Store class name in local list
66
        Entity.entityClassNameList.append(e)
67

Guilhem Saurel's avatar
Guilhem Saurel committed
68

69
# --- ENTITY -------------------------------------------------------------------
70

Guilhem Saurel's avatar
Guilhem Saurel committed
71

72
class VerbosityLevel(Enum):
73
    """
74
75
    Enum class for setVerbosityLevel
    """
Guilhem Saurel's avatar
Guilhem Saurel committed
76
    VERBOSITY_ALL = 0
77
78
    VERBOSITY_INFO_WARNING_ERROR = 1
    VERBOSITY_WARNING_ERROR = 2
79
    VERBOSITY_ERROR = 3
80
81
    VERBOSITY_NONE = 4

Guilhem Saurel's avatar
Guilhem Saurel committed
82
83

class Entity(object):
84
85
86
    """
    This class binds dynamicgraph::Entity C++ class
    """
florent's avatar
florent committed
87

88
    obj = None
89
90
91
    """
    Store list of entities created via python
    """
Guilhem Saurel's avatar
Guilhem Saurel committed
92
    entities = dict()
93

florent's avatar
florent committed
94
    def __init__(self, className, instanceName):
95
96
97
98
        """
        Constructor: if not called by a child class, create and store a pointer
        to a C++ Entity object.
        """
Guilhem Saurel's avatar
Guilhem Saurel committed
99
100
        object.__setattr__(self, 'obj', wrap.create_entity(className, instanceName))
        Entity.entities[instanceName] = self
101

102
103
104
105
106
107
108
109
110
111
    @staticmethod
    def initEntity(self, name):
        """
        Common constructor of specialized Entity classes. This function is bound
        by the factory to each new class derivated from the Entity class as the
        constructor of the new class.
        """
        Entity.__init__(self, self.className, name)
        if not self.__class__.commandCreated:
            self.boundClassCommands()
Guilhem Saurel's avatar
Guilhem Saurel committed
112
            self.__class__.__doc__ = wrap.entity_get_docstring(self.obj)
113
114
            self.__class__.commandCreated = True

115
    @property
Guilhem Saurel's avatar
Guilhem Saurel committed
116
    def name(self):
117
        return wrap.entity_get_name(self.obj)
118

119
    @property
Guilhem Saurel's avatar
Guilhem Saurel committed
120
    def className(self):
121
122
        return wrap.entity_get_class_name(self.obj)

Guilhem Saurel's avatar
Guilhem Saurel committed
123
    def __str__(self):
124
125
        return wrap.display_entity(self.obj)

Guilhem Saurel's avatar
Guilhem Saurel committed
126
    def signal(self, name):
127
128
129
        """
        Get a signal of the entity from signal name
        """
130
        signalPt = wrap.entity_get_signal(self.obj, name)
Guilhem Saurel's avatar
Guilhem Saurel committed
131
        return signal_base.SignalBase(name="", obj=signalPt)
florent's avatar
florent committed
132

Guilhem Saurel's avatar
Guilhem Saurel committed
133
    def hasSignal(self, name):
134
135
136
137
138
        """
        Indicates if a signal with the given name exists in the entity
        """
        return wrap.entity_has_signal(self.obj, name)

Guilhem Saurel's avatar
Guilhem Saurel committed
139
    def displaySignals(self):
140
        """
Nicolas Mansard's avatar
Nicolas Mansard committed
141
        Print the list of signals into standard output: temporary.
142
        """
143
        signals = list(self.signals())
144
        if len(signals) == 0:
Guilhem Saurel's avatar
Guilhem Saurel committed
145
            display("--- <" + self.name + "> has no signal")
146
        else:
Guilhem Saurel's avatar
Guilhem Saurel committed
147
148
149
150
            display("--- <" + self.name + "> signal list: ")
            for s in signals[:-1]:
                display("    |-- <" + str(s))
            display("    `-- <" + str(signals[-1]))
151

Guilhem Saurel's avatar
Guilhem Saurel committed
152
    def signals(self):
Nicolas Mansard's avatar
Nicolas Mansard committed
153
        """
154
        Return the list of signals
Nicolas Mansard's avatar
Nicolas Mansard committed
155
        """
156
        sl = wrap.entity_list_signals(self.obj)
Florent Lamiraux's avatar
Florent Lamiraux committed
157
        return map(lambda pyObj: signal_base.SignalBase(obj=pyObj), sl)
158
159

    def commands(self):
Nicolas Mansard's avatar
Nicolas Mansard committed
160
161
162
        """
        Return the list of commands.
        """
163
164
165
        return wrap.entity_list_commands(self.obj)

    def globalHelp(self):
Nicolas Mansard's avatar
Nicolas Mansard committed
166
167
168
        """
        Print a short description of each command.
        """
Guilhem Saurel's avatar
Guilhem Saurel committed
169
170
171
172
        if self.__doc__:
            print(self.__doc__)
        print("List of commands:")
        print("-----------------")
173
        for cstr in self.commands():
Guilhem Saurel's avatar
Guilhem Saurel committed
174
175
176
177
178
179
            ctitle = cstr + ':'
            for i in range(len(cstr), 15):
                ctitle += ' '
            for docstr in wrap.entity_get_command_docstring(self.obj, cstr).split('\n'):
                if (len(docstr) > 0) and (not docstr.isspace()):
                    display(ctitle + "\t" + docstr)
180
181
                    break

Guilhem Saurel's avatar
Guilhem Saurel committed
182
    def help(self, comm=None):
Nicolas Mansard's avatar
Nicolas Mansard committed
183
184
185
186
        """
        With no arg, print the global help. With arg the name of
        a specific command, print the help associated to the command.
        """
187
188
189
        if comm is None:
            self.globalHelp()
        else:
Guilhem Saurel's avatar
Guilhem Saurel committed
190
            display(comm + ":\n" + wrap.entity_get_command_docstring(self.obj, comm))
191
192

    def __getattr__(self, name):
193
        try:
194
            return self.signal(name)
Guilhem Saurel's avatar
Guilhem Saurel committed
195
        except Exception:
196
197
198
            try:
                object.__getattr__(self, name)
            except AttributeError:
Guilhem Saurel's avatar
Guilhem Saurel committed
199
200
201
                raise AttributeError("'%s' entity has no attribute %s\n" % (self.name, name) +
                                     '  entity attributes are usually either\n' + '    - commands,\n' +
                                     '    - signals or,\n' + '    - user defined attributes')
202

203
    def __setattr__(self, name, value):
Guilhem Saurel's avatar
Guilhem Saurel committed
204
205
        if name in map(lambda s: s.getName().split(':')[-1], self.signals()):
            raise NameError(name + " already designates a signal. "
206
207
208
                            "It is not advised to set a new attribute of the same name.")
        object.__setattr__(self, name, value)

209
210
211
212
213
214
215
    # --- COMMANDS BINDER -----------------------------------------------------
    # List of all the entity classes from the c++ factory, that have been bound
    # bind the py factory.
    entityClassNameList = []

    # This function dynamically create the function object that runs the command.
    @staticmethod
Guilhem Saurel's avatar
Guilhem Saurel committed
216
    def createCommandBind(name, docstring):
217
218
        def commandBind(self, *arg):
            return wrap.entity_execute_command(self.obj, name, arg)
Guilhem Saurel's avatar
Guilhem Saurel committed
219

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
        commandBind.__doc__ = docstring
        return commandBind

    def boundClassCommands(self):
        """
        This static function has to be called from a class heritating from Entity.
        It should be called only once. It parses the list of commands obtained from
        c++, and bind each of them to a python class method.
        """
        # Get list of commands of the Entity object
        commands = wrap.entity_list_commands(self.obj)
        # for each command, add a method with the name of the command
        for cmdstr in commands:
            docstr = wrap.entity_get_command_docstring(self.obj, cmdstr)
            cmdpy = Entity.createCommandBind(cmdstr, docstr)
            setattrpath(self.__class__, cmdstr, cmdpy)

Guilhem Saurel's avatar
Guilhem Saurel committed
237
    def boundNewCommand(self, cmdName):
238
239
240
241
242
        """
        At construction, all existing commands are bound directly in the class.
        This method enables to bound new commands dynamically. These new bounds
        are not made with the class, but directly with the object instance.
        """
243
        if (cmdName in self.__dict__) | (cmdName in self.__class__.__dict__):
Guilhem Saurel's avatar
Guilhem Saurel committed
244
            print("Warning: command ", cmdName, " will overwrite an object attribute.")
245
        docstring = wrap.entity_get_command_docstring(self.obj, cmdName)
Guilhem Saurel's avatar
Guilhem Saurel committed
246
        cmd = Entity.createCommandBind(cmdName, docstring)
247
        # Limitation (todo): does not handle for path attribute name (see setattrpath).
Guilhem Saurel's avatar
Guilhem Saurel committed
248
        setattr(self, cmdName, types.MethodType(cmd, self))
249

250
251
252
253
254
255
    def boundAllNewCommands(self):
        """
        For all commands that are not attribute of the object instance nor of the
        class, a new attribute of the instance is created to bound the command.
        """
        cmdList = wrap.entity_list_commands(self.obj)
Guilhem Saurel's avatar
Guilhem Saurel committed
256
257
        cmdList = filter(lambda x: x not in self.__dict__, cmdList)
        cmdList = filter(lambda x: x not in self.__class__.__dict__, cmdList)
258
        for cmd in cmdList:
Guilhem Saurel's avatar
Guilhem Saurel committed
259
            self.boundNewCommand(cmd)
260

Guilhem Saurel's avatar
Guilhem Saurel committed
261
    def setLoggerVerbosityLevel(self, verbosity):
262
        """
263
264
265
        Specify for the entity the verbosity level.
        - param verbosity should be one of the attribute of the enum
                dynamic_graph.entity.VerbosityLevel
266
        """
267
        return wrap.entity_set_logger_verbosity(self.obj, verbosity)
268
269

    def getLoggerVerbosityLevel(self):
270
        """
271
        Returns the entity's verbosity level (as a dynamic_graph.entity.VerbosityLevel)
272
        """
Guilhem Saurel's avatar
Guilhem Saurel committed
273
274
        r = wrap.entity_get_logger_verbosity(self.obj)
        if r == 0:
275
            return VerbosityLevel.VERBOSITY_ALL
Guilhem Saurel's avatar
Guilhem Saurel committed
276
        elif r == 1:
277
            return VerbosityLevel.VERBOSITY_INFO_WARNING_ERROR
Guilhem Saurel's avatar
Guilhem Saurel committed
278
        elif r == 2:
279
            return VerbosityLevel.VERBOSITY_WARNING_ERROR
Guilhem Saurel's avatar
Guilhem Saurel committed
280
        elif r == 3:
281
282
            return VerbosityLevel.VERBOSITY_ERROR
        return VerbosityLevel.VERBOSITY_NONE
283

Guilhem Saurel's avatar
Guilhem Saurel committed
284
    def setTimeSample(self, timeSample):
285
286
287
288
289
290
291
292
293
294
295
        """
        Specify for the entity the time at which call is counted.
        """
        return wrap.entity_set_time_sample(self.obj, timeSample)

    def getTimeSample(self):
        """
        Returns for the entity the time at which call is counted.
        """
        return wrap.entity_get_time_sample(self.obj)

Guilhem Saurel's avatar
Guilhem Saurel committed
296
    def setStreamPrintPeriod(self, streamPrintPeriod):
297
298
299
300
301
302
303
304
305
306
        """
        Specify for the entity the period at which debugging information is printed
        """
        return wrap.entity_set_stream_print_period(self.obj, streamPrintPeriod)

    def getStreamPrintPeriod(self):
        """
        Returns for the entity the period at which debugging information is printed
        """
        return wrap.entity_get_stream_print_period(self.obj)