component.py
Go to the documentation of this file.
1 # -*- Python -*-
2 # -*- coding: utf-8 -*-
3 
4 '''rtctree
5 
6 Copyright (C) 2009-2014
7  Geoffrey Biggs
8  RT-Synthesis Research Group
9  Intelligent Systems Research Institute,
10  National Institute of Advanced Industrial Science and Technology (AIST),
11  Japan
12  All rights reserved.
13 Licensed under the Eclipse Public License -v 1.0 (EPL)
14 http://www.opensource.org/licenses/eclipse-1.0.txt
15 
16 Object representing a component node in the tree.
17 
18 '''
19 
20 
21 import RTC
22 import SDOPackage
23 import time
24 import uuid
25 
26 from rtctree.config_set import ConfigurationSet
27 from rtctree.exceptions import *
28 from rtctree.exec_context import ExecutionContext
29 from rtctree.node import TreeNode
30 import rtctree.sdo
31 from rtctree.ports import parse_port
32 from rtctree.utils import build_attr_string, nvlist_to_dict, dict_to_nvlist
33 
34 
35 ##############################################################################
36 ## Component node object
37 
39  '''Node representing a component on a name server.
40 
41  Component nodes can occur below name server and directory nodes. They
42  cannot contain any children.
43 
44  Component nodes store all the properties of a component, as well as
45  references to the actual objects and object types used in the component.
46 
47  This node can be made dynamic by setting the 'dynamic' property to True.
48  The following callbacks are available on this node type when it is dynamic:
49 
50  - rtc_status(ec_handle, status)
51  The component's state has changed in the specified execution context. The
52  new status is one of Component.INACTIVE, Component.ACTIVE,
53  Component.ERROR, Component.UNKNOWN and Component.CREATED.
54  - component_profile(items)
55  One or more members of the component's profiles have been updated. The
56  updated items are listed in the "items" argument.
57  - ec_event(ec_handle, event)
58  A change in one of the attached execution contexts has occurred. The
59  event is one of Component.EC_ATTACHED, Component.EC_DETACHED,
60  Component.EC_RATE_CHANGED, Component.EC_STARTUP, and
61  Component.EC_SHUTDOWN.
62  - port_event(port_name, event)
63  A change on the specified port has occurred. The event is one of
64  Component.PORT_ADD, Component.PORT_REMOVE, Component.PORT_CONNECT and
65  Component.PORT_DISCONNECT.
66  - config_event(config_set_name, event)
67  A change in the component's configuration sets has occurred. The event is
68  one of Component.CFG_UPDATE_SET, Component.CFG_UPDATE_PARAM,
69  Component.CFG_UPDATE_PARAM_IN_ACTIVE, Component.CFG_ADD_SET,
70  Component.CFG_REMOVE_SET and Component.CFG_ACTIVATE_SET.
71  - heartbeat(time)
72  A heartbeat was received from the component. The time the beat was
73  received is passed.
74 
75  '''
76  def __init__(self, name=None, parent=None, obj=None, *args, **kwargs):
77  '''Constructor.
78 
79  @param name Name of this component (i.e. its entry in the path).
80  @param parent The parent node of this node, if any.
81  @param obj The CORBA LightweightRTObject object to wrap.
82 
83  '''
84  self._obj = obj
85  self._obs = None
86  self._obs_id = None
87  self._loggers = {}
88  self._last_heartbeat = time.time() # RTC is alive at construction time
89  super(Component, self).__init__(name=name, parent=parent,
90  *args, **kwargs)
91  self._set_events(['rtc_status', 'component_profile', 'ec_event',
92  'port_event', 'config_event', 'heartbeat'])
93  self._reset_data()
94  self._parse_profile()
95 
96  def reparse(self):
97  '''Reparse the component's information.
98 
99  This will cause a delayed reparse of most information. This means that
100  a piece of information, such as the list of ports, will be cleared and
101  remain empty until it is next requested, at which point a fresh list
102  will be retrieved from the component.
103 
104  If you only want to reparse a specific piece of information, use one of
105  the reparse_X() methods.
106 
107  '''
108  self._reset_data()
109  self._parse_profile()
110 
111  def reparse_conf_sets(self):
112  '''Delayed reparse configuration sets.'''
113  self._reset_conf_sets()
114 
115  def reparse_ecs(self):
116  '''Delayed reparse all execution contexts.'''
117  self.reparse_owned_ecs()
119 
120  def reparse_owned_ecs(self):
121  '''Delayed reparse only owned execution contexts.'''
122  self._reset_owned_ecs()
123 
125  '''Delayed reparse only participating execution contexts.'''
127 
128  def reparse_ports(self):
129  '''Delayed reparse ports.'''
130  self._reset_ports()
131 
132  def reparse_profile(self):
133  '''Delayed reparse the component's profile.'''
134  self._parse_profile()
135 
136  ###########################################################################
137  # Component information
138 
139  @property
140  def category(self):
141  '''The category in which the component belongs.'''
142  with self._mutex:
143  return self._category
144 
145  @property
146  def description(self):
147  '''The component's description.'''
148  with self._mutex:
149  return self._description
150 
151  @property
152  def instance_name(self):
153  '''Instance name of the component.'''
154  with self._mutex:
155  return self._instance_name
156 
157  @property
158  def parent_object(self):
159  '''The name of the component's parent object (typically another
160  component), if it has one.
161 
162  '''
163  with self._mutex:
164  return self._parent_obj
165 
166  @property
167  def properties(self):
168  '''The component's extra properties dictionary.'''
169  with self._mutex:
170  return self._properties
171 
172  @property
173  def type_name(self):
174  '''Type name of the component.'''
175  with self._mutex:
176  return self._type_name
177 
178  @property
179  def vendor(self):
180  '''The component's vendor.'''
181  with self._mutex:
182  return self._vendor
183 
184  @property
185  def version(self):
186  '''The component's version.'''
187  with self._mutex:
188  return self._version
189 
190  ###########################################################################
191  # Composite component information and management
192 
193  def add_members(self, rtcs):
194  '''Add other RT Components to this composite component as members.
195 
196  This component must be a composite component.
197 
198  '''
199  if not self.is_composite:
200  raise NotCompositeError(self.name)
201  for rtc in rtcs:
202  if self.is_member(rtc):
203  raise AlreadyInCompositionError(self.name, rtc.instance_name)
204  org = self.organisations[0].obj
205  org.add_members([x.object for x in rtcs])
206  # Force a reparse of the member information
207  self._orgs = []
208 
209  def remove_members(self, rtcs):
210  '''Remove other RT Components from this composite component.
211 
212  rtcs is a list of components to remove. Each element must be either an
213  rtctree.Component object or a string containing a component's instance
214  name. rtctree.Component objects are more reliable.
215 
216  This component must be a composite component.
217 
218  '''
219  if not self.is_composite:
220  raise NotCompositeError(self.name)
221  org = self.organisations[0].obj
222  members = org.get_members()
223  for rtc in rtcs:
224  if type(rtc) == str:
225  rtc_name = rtc
226  else:
227  rtc_name = rtc.instance_name
228  # Check if the RTC actually is a member
229  if not self.is_member(rtc):
230  raise NotInCompositionError(self.name, rtc_name)
231  # Remove the RTC from the composition
232  org.remove_member(rtc_name)
233  # Force a reparse of the member information
234  self._orgs = []
235 
236  @property
237  def composite_parent(self):
238  '''The parent component in the composition.
239 
240  None if this component is not a member of a composition.
241 
242  '''
243  return None
244 
245  @property
246  def is_composite(self):
247  '''Is the component a composite component.'''
248  return self._obj.get_owned_organizations() != []
249 
250  @property
252  '''Is the component a member of a composite component.'''
253  return self._obj.get_organizations() != []
254 
255  def is_member(self, rtc):
256  '''Is the given component a member of this composition?
257 
258  rtc may be a Component object or a string containing a component's
259  instance name. Component objects are more reliable.
260 
261  Returns False if the given component is not a member of this
262  composition.
263 
264  Raises NotCompositeError if this component is not a composition.
265 
266  '''
267  if not self.is_composite:
268  raise NotCompositeError(self.name)
269  members = self.organisations[0].obj.get_members()
270  if type(rtc) is str:
271  for m in members:
272  if m.get_component_profile().instance_name == rtc:
273  return True
274  else:
275  for m in members:
276  if m._is_equivalent(rtc.object):
277  return True
278  return False
279 
280  @property
281  def members(self):
282  '''Member components if this component is composite.'''
283  with self._mutex:
284  if not self._members:
285  self._members = {}
286  for o in self.organisations:
287  # TODO: Search for these in the tree
288  self._members[o.org_id] = o.obj.get_members()
289  return self._members
290 
291  @property
292  def organisations(self):
293  '''The organisations of this composition.'''
294  class Org:
295  def __init__(self, sdo_id, org_id, members, obj):
296  self.sdo_id = sdo_id
297  self.org_id = org_id
298  self.members = members
299  self.obj = obj
300 
301  with self._mutex:
302  if not self._orgs:
303  for org in self._obj.get_owned_organizations():
304  owner = org.get_owner()
305  if owner:
306  sdo_id = owner._narrow(SDOPackage.SDO).get_sdo_id()
307  else:
308  sdo_id = ''
309  org_id = org.get_organization_id()
310  members = [m.get_sdo_id() for m in org.get_members()]
311  self._orgs.append(Org(sdo_id, org_id, members, org))
312  return self._orgs
313 
314  @property
315  def org_ids(self):
316  '''The organisation IDs of this composition.'''
317  return [sdo.get_organization_id() for sdo in \
318  self._obj.get_owned_organizations()]
319 
320  @property
321  def parent_org_ids(self):
322  '''The organisation IDs of the compositions this RTC belongs to.'''
323  return [sdo.get_organization_id() for sdo in \
324  self._obj.get_organizations() if sdo]
325 
326  @property
328  '''The SDO IDs of the compositions this RTC belongs to.'''
329  return [sdo.get_owner()._narrow(SDOPackage.SDO).get_sdo_id() \
330  for sdo in self._obj.get_organizations() if sdo]
331 
332  @property
334  '''The organisations this RTC belongs to.'''
335  class ParentOrg:
336  def __init__(self, sdo_id, org_id):
337  self.sdo_id = sdo_id
338  self.org_id = org_id
339 
340  with self._mutex:
341  if not self._parent_orgs:
342  for sdo in self._obj.get_organizations():
343  if not sdo:
344  continue
345  owner = sdo.get_owner()
346  if owner:
347  sdo_id = owner._narrow(SDOPackage.SDO).get_sdo_id()
348  else:
349  sdo_id = ''
350  org_id = sdo.get_organization_id()
351  self._parent_orgs.append(ParentOrg(sdo_id, org_id))
352  return self._parent_orgs
353 
354  ###########################################################################
355  # State management
356 
357  def exit(self):
358  '''Stop the component's execution contexts and finalise it.
359 
360  This will have flow-on effects to any other components using this
361  component's execution contexts and any child components.
362 
363  @return The result of attempting to exit.
364 
365  '''
366  with self._mutex:
367  return self._obj.exit()
368 
369  def activate_in_ec(self, ec_index):
370  '''Activate this component in an execution context.
371 
372  @param ec_index The index of the execution context to activate in.
373  This index is into the total array of contexts, that
374  is both owned and participating contexts. If the value
375  of ec_index is greater than the length of
376  @ref owned_ecs, that length is subtracted from
377  ec_index and the result used as an index into
378  @ref participating_ecs.
379 
380  '''
381  with self._mutex:
382  if ec_index >= len(self.owned_ecs):
383  ec_index -= len(self.owned_ecs)
384  if ec_index >= len(self.participating_ecs):
385  raise BadECIndexError(ec_index)
386  ec = self.participating_ecs[ec_index]
387  else:
388  ec = self.owned_ecs[ec_index]
389  ec.activate_component(self._obj)
390 
391  def deactivate_in_ec(self, ec_index):
392  '''Deactivate this component in an execution context.
393 
394  @param ec_index The index of the execution context to deactivate in.
395  This index is into the total array of contexts, that
396  is both owned and participating contexts. If the value
397  of ec_index is greater than the length of
398  @ref owned_ecs, that length is subtracted from
399  ec_index and the result used as an index into
400  @ref participating_ecs.
401 
402  '''
403  with self._mutex:
404  if ec_index >= len(self.owned_ecs):
405  ec_index -= len(self.owned_ecs)
406  if ec_index >= len(self.participating_ecs):
407  raise BadECIndexError(ec_index)
408  ec = self.participating_ecs[ec_index]
409  else:
410  ec = self.owned_ecs[ec_index]
411  ec.deactivate_component(self._obj)
412 
413  def exit(self):
414  '''Make a component exit.
415 
416  This function will make the component exit, shutting down its CORBA
417  object in the process. It will not remove the node from the tree; a
418  reparse is necessary to do that.
419 
420  '''
421  self._obj.exit()
422 
423  def get_ec(self, ec_handle):
424  '''Get a reference to the execution context with the given handle.
425 
426  @param ec_handle The handle of the execution context to look for.
427  @type ec_handle str
428  @return A reference to the ExecutionContext object corresponding to
429  the ec_handle.
430  @raises NoECWithHandleError
431 
432  '''
433  with self._mutex:
434  for ec in self.owned_ecs:
435  if ec.handle == ec_handle:
436  return ec
437  for ec in self.participating_ecs:
438  if ec.handle == ec_handle:
439  return ec
440  raise NoECWithHandleError
441 
442 
443  def get_ec_index(self, ec_handle):
444  '''Get the index of the execution context with the given handle.
445 
446  @param ec_handle The handle of the execution context to look for.
447  @type ec_handle str
448  @return The index into the owned + participated arrays, suitable for
449  use in methods such as @ref activate_in_ec, or -1 if the EC was not
450  found.
451  @raises NoECWithHandleError
452 
453  '''
454  with self._mutex:
455  for ii, ec in enumerate(self.owned_ecs):
456  if ec.handle == ec_handle:
457  return ii
458  for ii, ec in enumerate(self.participating_ecs):
459  if ec.handle == ec_handle:
460  return ii + len(self.owned_ecs)
461  raise NoECWithHandleError
462 
463  def get_state_string(self, add_colour=True):
464  '''Get the state of this component as an optionally-coloured string.
465 
466  @param add_colour If True, ANSI colour codes will be added to the
467  string.
468  @return A string describing the state of this component.
469 
470  '''
471  with self._mutex:
472  if self.state == self.INACTIVE:
473  result = 'Inactive', ['bold', 'blue']
474  elif self.state == self.ACTIVE:
475  result = 'Active', ['bold', 'green']
476  elif self.state == self.ERROR:
477  result = 'Error', ['bold', 'white', 'bgred']
478  elif self.state == self.UNKNOWN:
479  result = 'Unknown', ['bold', 'red']
480  elif self.state == self.CREATED:
481  result = 'Created', ['reset']
482  if add_colour:
483  return build_attr_string(result[1], supported=add_colour) + \
484  result[0] + build_attr_string('reset', supported=add_colour)
485  else:
486  return result[0]
487 
488  def get_state_in_ec_string(self, ec_index, add_colour=True):
489  '''Get the state of the component in an execution context as a string.
490 
491  @param ec_index The index of the execution context to check the state
492  in. This index is into the total array of contexts,
493  that is both owned and participating contexts. If the
494  value of ec_index is greater than the length of @ref
495  owned_ecs, that length is subtracted from ec_index and
496  the result used as an index into @ref
497  participating_ecs.
498 
499  '''
500  with self._mutex:
501  if ec_index >= len(self.owned_ecs):
502  ec_index -= len(self.owned_ecs)
503  if ec_index >= len(self.participating_ecs):
504  raise BadECIndexError(ec_index)
505  state = self.participating_ec_states[ec_index]
506  else:
507  state = self.owned_ec_states[ec_index]
508  if state == self.INACTIVE:
509  result = 'Inactive', ['bold', 'blue']
510  elif state == self.ACTIVE:
511  result = 'Active', ['bold', 'green']
512  elif state == self.ERROR:
513  result = 'Error', ['bold', 'white', 'bgred']
514  elif state == self.UNKNOWN:
515  result = 'Unknown', ['bold', 'red']
516  elif state == self.CREATED:
517  result = 'Created', ['reset']
518  if add_colour:
519  return build_attr_string(result[1], supported=add_colour) + \
520  result[0] + build_attr_string('reset',
521  supported=add_colour)
522  else:
523  return result[0]
524 
525  def reset_in_ec(self, ec_index):
526  '''Reset this component in an execution context.
527 
528  @param ec_index The index of the execution context to reset in. This
529  index is into the total array of contexts, that is both
530  owned and participating contexts. If the value of
531  ec_index is greater than the length of @ref owned_ecs,
532  that length is subtracted from ec_index and the result
533  used as an index into @ref participating_ecs.
534 
535  '''
536  with self._mutex:
537  if ec_index >= len(self.owned_ecs):
538  ec_index -= len(self.owned_ecs)
539  if ec_index >= len(self.participating_ecs):
540  raise BadECIndexError(ec_index)
541  ec = self.participating_ecs[ec_index]
542  else:
543  ec = self.owned_ecs[ec_index]
544  ec.reset_component(self._obj)
545 
546  def state_in_ec(self, ec_index):
547  '''Get the state of the component in an execution context.
548 
549  @param ec_index The index of the execution context to check the state
550  in. This index is into the total array of contexts,
551  that is both owned and participating contexts. If the
552  value of ec_index is greater than the length of @ref
553  owned_ecs, that length is subtracted from ec_index and
554  the result used as an index into @ref
555  participating_ecs.
556 
557  '''
558  with self._mutex:
559  if ec_index >= len(self.owned_ecs):
560  ec_index -= len(self.owned_ecs)
561  if ec_index >= len(self.participating_ecs):
562  raise BadECIndexError(ec_index)
563  return self.participating_ec_states[ec_index]
564  else:
565  return self.owned_ec_states[ec_index]
566 
567  def refresh_state_in_ec(self, ec_index):
568  '''Get the up-to-date state of the component in an execution context.
569 
570  This function will update the state, rather than using the cached
571  value. This may take time, if the component is executing on a remote
572  node.
573 
574  @param ec_index The index of the execution context to check the state
575  in. This index is into the total array of contexts,
576  that is both owned and participating contexts. If the
577  value of ec_index is greater than the length of @ref
578  owned_ecs, that length is subtracted from ec_index and
579  the result used as an index into @ref
580  participating_ecs.
581 
582  '''
583  with self._mutex:
584  if ec_index >= len(self.owned_ecs):
585  ec_index -= len(self.owned_ecs)
586  if ec_index >= len(self.participating_ecs):
587  raise BadECIndexError(ec_index)
588  state = self._get_ec_state(self.participating_ecs[ec_index])
589  self.participating_ec_states[ec_index] = state
590  else:
591  state = self._get_ec_state(self.owned_ecs[ec_index])
592  self.owned_ec_states[ec_index] = state
593  return state
594 
595  @property
596  def alive(self):
597  '''Is this component alive?'''
598  with self._mutex:
599  if self.exec_contexts:
600  for ec in self.exec_contexts:
601  if self._obj.is_alive(ec):
602  return True
603  return False
604 
605  @property
606  def owned_ec_states(self):
607  '''The state of each execution context this component owns.'''
608  with self._mutex:
609  if not self._owned_ec_states:
610  if self.owned_ecs:
611  states = []
612  for ec in self.owned_ecs:
613  states.append(self._get_ec_state(ec))
614  self._owned_ec_states = states
615  else:
616  self._owned_ec_states = []
617  return self._owned_ec_states
618 
619  @property
620  def owned_ecs(self):
621  '''A list of the execution contexts owned by this component.'''
622  with self._mutex:
623  if not self._owned_ecs:
625  self._obj.get_context_handle(ec)) \
626  for ec in self._obj.get_owned_contexts()]
627  return self._owned_ecs
628 
629  @property
631  '''The state of each execution context this component is participating
632  in.
633 
634  '''
635  with self._mutex:
636  if not self._participating_ec_states:
637  if self.participating_ecs:
638  states = []
639  for ec in self.participating_ecs:
640  states.append(self._get_ec_state(ec))
642  else:
643  self._participating_ec_states = []
644  return self._participating_ec_states
645 
646  @property
647  def participating_ecs(self):
648  '''A list of the execution contexts this component is participating in.
649 
650  '''
651  with self._mutex:
652  if not self._participating_ecs:
654  self._obj.get_context_handle(ec)) \
655  for ec in self._obj.get_participating_contexts()]
656  return self._participating_ecs
657 
658  @property
660  '''The state of this component as a string without colour.'''
661  return self.get_state_string(add_colour=False)
662 
663  @property
664  def state(self):
665  '''The merged state of all the execution context states, which can be
666  used as the overall state of this component.
667 
668  The order of precedence is:
669  Error > Active > Inactive > Created > Unknown
670 
671  '''
672  def merge_state(current, new):
673  if new == self.ERROR:
674  return self.ERROR
675  elif new == self.ACTIVE and current != self.ERROR:
676  return self.ACTIVE
677  elif new == self.INACTIVE and \
678  current not in [self.ACTIVE, self.ERROR]:
679  return self.INACTIVE
680  elif new == self.CREATED and \
681  current not in [self.ACTIVE, self.ERROR, self.INACTIVE]:
682  return self.CREATED
683  elif current not in [self.ACTIVE, self.ERROR, self.INACTIVE,
684  self.CREATED]:
685  return self.UNKNOWN
686  return current
687 
688  with self._mutex:
689  if not self.owned_ec_states and not self.participating_ec_states:
690  return self.UNKNOWN
691  merged_state = self.CREATED
692  if self.owned_ec_states:
693  for ec_state in self.owned_ec_states:
694  merged_state = merge_state(merged_state, ec_state)
695  if self.participating_ec_states:
696  for ec_state in self.participating_ec_states:
697  merged_state = merge_state(merged_state, ec_state)
698  return merged_state
699 
700  @property
701  def state_string(self):
702  '''The state of this component as a colourised string.'''
703  return self.get_state_string()
704 
705  ###########################################################################
706  # Port management
707 
708  def disconnect_all(self):
709  '''Disconnect all connections to all ports of this component.'''
710  with self._mutex:
711  for p in self.ports:
712  p.disconnect_all()
713 
714  def get_port_by_name(self, port_name):
715  '''Get a port of this component by name.'''
716  with self._mutex:
717  for p in self.ports:
718  if p.name == port_name:
719  return p
720  return None
721 
722  def get_port_by_ref(self, port_ref):
723  '''Get a port of this component by reference to a CORBA PortService
724  object.
725 
726  '''
727  with self._mutex:
728  for p in self.ports:
729  if p.object._is_equivalent(port_ref):
730  return p
731  return None
732 
733  def has_port_by_name(self, port_name):
734  '''Check if this component has a port by the given name.'''
735  with self._mutex:
736  if self.get_port_by_name(port_name):
737  return True
738  return False
739 
740  def has_port_by_ref(self, port_ref):
741  '''Check if this component has a port by the given reference to a CORBA
742  PortService object.
743 
744  '''
745  with self._mutex:
746  if self.get_port_by_ref(self, port_ref):
747  return True
748  return False
749 
750  @property
751  def connected_inports(self):
752  '''The list of all input ports belonging to this component that are
753  connected to one or more other ports.
754 
755  '''
756  return [p for p in self.ports \
757  if p.__class__.__name__ == 'DataInPort' and p.is_connected]
758 
759  @property
761  '''The list of all output ports belonging to this component that are
762  connected to one or more other ports.
763 
764  '''
765  return [p for p in self.ports \
766  if p.__class__.__name__ == 'DataOutPort' \
767  and p.is_connected]
768 
769  @property
770  def connected_ports(self):
771  '''The list of all ports belonging to this component that are connected
772  to one or more other ports.
773 
774  '''
775  return [p for p in self.ports if p.is_connected]
776 
777  @property
779  '''The list of all service ports belonging to this component that are
780  connected to one or more other ports.
781 
782  '''
783  return [p for p in self.ports \
784  if p.__class__.__name__ == 'CorbaPort' and p.is_connected]
785 
786  @property
787  def inports(self):
788  '''The list of all input ports belonging to this component.'''
789  return [p for p in self.ports if p.__class__.__name__ == 'DataInPort']
790 
791  @property
792  def outports(self):
793  '''The list of all output ports belonging to this component.'''
794  return [p for p in self.ports if p.__class__.__name__ == 'DataOutPort']
795 
796  @property
797  def ports(self):
798  '''The list of all ports belonging to this component.'''
799  with self._mutex:
800  if not self._ports:
801  self._ports = [parse_port(port, self) \
802  for port in self._obj.get_ports()]
803  return self._ports
804 
805  @property
806  def svcports(self):
807  '''The list of all service ports belonging to this component.'''
808  return [p for p in self.ports if p.__class__.__name__ == 'CorbaPort']
809 
810  ###########################################################################
811  # Node functionality
812 
813  @property
814  def heartbeat_time(self):
815  '''The time of the last heartbeat.
816 
817  Updated only when the node is dynamic.
818 
819  '''
820  return self._last_heartbeat
821 
822  @property
823  def is_component(self):
824  '''Is this node a component?'''
825  return True
826 
827  @property
828  def loggers(self):
829  '''Returns the list of logger IDs attached to this component.'''
830  return self._loggers.keys()
831 
832  @property
833  def object(self):
834  '''The LightweightRTObject this object wraps.'''
835  with self._mutex:
836  return self._obj
837 
838  def add_logger(self, cb, level='NORMAL', filters='ALL'):
839  '''Add a callback to receive log events from this component.
840 
841  @param cb The callback function to receive log events. It must have the
842  signature cb(name, time, source, level, message), where name is the
843  name of the component the log record came from, time is a
844  floating-point time stamp, source is the name of the logger that
845  provided the log record, level is the log level of the record and
846  message is a text string.
847  @param level The maximum level of log records to receive.
848  @param filters Filter the objects from which to receive log messages.
849  @return An ID for this logger. Use this ID in future operations such as
850  removing this logger.
851  @raises AddLoggerError
852 
853  '''
854  with self._mutex:
855  obs = rtctree.sdo.RTCLogger(self, cb)
856  uuid_val = uuid.uuid4()
857  intf_type = obs._this()._NP_RepositoryId
858  props = {'logger.log_level': level,
859  'logger.filter': filters}
860  props = dict_to_nvlist(props)
861  sprof = SDOPackage.ServiceProfile(id=uuid_val.get_bytes(),
862  interface_type=intf_type, service=obs._this(),
863  properties=props)
864  conf = self.object.get_configuration()
865  res = conf.add_service_profile(sprof)
866  if res:
867  self._loggers[uuid_val] = obs
868  return uuid_val
869  raise AddLoggerError(self.name)
870 
871  def remove_logger(self, cb_id):
872  '''Remove a logger.
873 
874  @param cb_id The ID of the logger to remove.
875  @raises NoLoggerError
876 
877  '''
878  if cb_id not in self._loggers:
879  raise NoLoggerError(cb_id, self.name)
880  conf = self.object.get_configuration()
881  res = conf.remove_service_profile(cb_id.get_bytes())
882  del self._loggers[cb_id]
883 
884  ###########################################################################
885  # Configuration set management
886 
887  def activate_conf_set(self, set_name):
888  '''Activate a configuration set by name.
889 
890  @raises NoSuchConfSetError
891 
892  '''
893  with self._mutex:
894  if not set_name in self.conf_sets:
895  raise NoSuchConfSetError(set_name)
896  self._conf.activate_configuration_set(set_name)
897 
898  def set_conf_set_value(self, set_name, param, value):
899  '''Set a configuration set parameter value.
900 
901  @param set_name The name of the configuration set the destination
902  parameter is in.
903  @param param The name of the parameter to set.
904  @param value The new value for the parameter.
905  @raises NoSuchConfSetError, NoSuchConfParamError
906 
907  '''
908  with self._mutex:
909  if not set_name in self.conf_sets:
910  raise NoSuchConfSetError(set_name)
911  if not self.conf_sets[set_name].has_param(param):
912  raise NoSuchConfParamError(param)
913  self.conf_sets[set_name].set_param(param, value)
914  self._conf.set_configuration_set_values(\
915  self.conf_sets[set_name].object)
916 
917  @property
918  def active_conf_set(self):
919  '''The currently-active configuration set.'''
920  with self._mutex:
921  if not self.conf_sets:
922  return None
923  if not self._active_conf_set:
924  return None
925  return self.conf_sets[self._active_conf_set]
926 
927  @property
929  '''The name of the currently-active configuration set.'''
930  with self._mutex:
931  if not self.conf_sets:
932  return ''
933  if not self._active_conf_set:
934  return ''
935  return self._active_conf_set
936 
937  @property
938  def conf_sets(self):
939  '''The dictionary of configuration sets in this component, if any.'''
940  with self._mutex:
941  if not self._conf_sets:
942  self._parse_configuration()
943  return self._conf_sets
944 
945  ###########################################################################
946  # Internal API
947 
948  def _add_child(self):
949  # Components cannot contain children.
950  raise CannotHoldChildrenError
951 
952  def _config_event(self, name, event):
953  with self._mutex:
954  if self._conf_sets:
955  if event == self.CFG_UPDATE_SET:
956  # A configuration set has been updated
957  cs = self._conf.get_configuration_set(name)
958  self._conf_sets[name]._reload(cs, cs.description,
959  nvlist_to_dict(cs.configuration_data))
960  elif event == self.CFG_UPDATE_PARAM:
961  # A parameter in a configuration set has been changed
962  cset, param = name.split('.')
963  cs = self._conf.get_configuration_set(cset)
964  data = nvlist_to_dict(cs.configuration_data)
965  self._conf_sets[cset].set_param(param, data[param])
966  elif event == self.CFG_ADD_SET:
967  # A new configuration set has been added
968  cs = self._conf.get_configuration_set(name)
969  self._conf_sets[name] = ConfigurationSet(self, cs,
970  cs.description,
971  nvlist_to_dict(cs.configuration_data))
972  elif event == self.CFG_REMOVE_SET:
973  # Remove the configuration set
974  del self._conf_sets[name]
975  elif event == self.CFG_ACTIVATE_SET:
976  # Change the active configuration set
977  self._active_conf_set = name
978  # Call callbacks outside the mutex
979  self._call_cb('config_event', (name, event))
980 
981  def _enable_dynamic(self, enable=True):
982  if enable:
983  obs = rtctree.sdo.RTCObserver(self)
984  uuid_val = uuid.uuid4().get_bytes()
985  intf_type = obs._this()._NP_RepositoryId
986  props = dict_to_nvlist({'heartbeat.enable': 'YES',
987  'heartbeat.interval': '1.0',
988  'observed_status': 'ALL'})
989  sprof = SDOPackage.ServiceProfile(id=uuid_val,
990  interface_type=intf_type, service=obs._this(),
991  properties=props)
992  conf = self.object.get_configuration()
993  res = conf.add_service_profile(sprof)
994  if res:
995  self._dynamic = True
996  self._obs = obs
997  self._obs_id = uuid_val
998  # If we could set an observer, the component is alive
999  self._last_heartbeat = time.time()
1000  else: # Disable
1001  conf = self.object.get_configuration()
1002  res = conf.remove_service_profile(self._obs_id)
1003  if res:
1004  self._dynamic = False
1005  self._obs = None
1006  self._obs_id = None
1007 
1008  def _ec_event(self, ec_handle, event):
1009  def get_ec(ec_handle):
1010  tgt_ec = None
1011  loc = None
1012  if self._owned_ecs:
1013  for ec in self._owned_ecs:
1014  if ec.handle == ec_handle:
1015  tgt_ec = ec
1016  loc = self._owned_ecs
1017  break
1018  if not del_ec and self._participating_ecs:
1019  for ec in self._participating_ecs:
1020  if ec.handle == ec_handle:
1021  tgt_ec = ec
1022  loc = self._participating_ecs
1023  break
1024  return tgt_ec, loc
1025 
1026  with self._mutex:
1027  if event == self.EC_ATTACHED:
1028  # New EC has been attached
1029  self._participating_ecs.append(ExecutionContext(
1030  self._obj.get_context(ec_handle), ec_handle))
1031  elif event == self.EC_DETACHED:
1032  # An EC has been detached; delete the local facade
1033  # if ec is not None, the corresponding EC has a local
1034  # facade that needs deleting
1035  ec, loc = get_ec(ec_handle)
1036  if ec:
1037  loc.remove(ec)
1038  elif event == self.EC_RATE_CHANGED:
1039  # Nothing to do
1040  pass
1041  elif event == self.EC_STARTUP:
1042  ec, loc = get_ec(ec_handle)
1043  if ec:
1044  ec._set_running(True)
1045  elif event == self.EC_SHUTDOWN:
1046  ec, loc = get_ec(ec_handle)
1047  if ec:
1048  ec._set_running(False)
1049  # Call callbacks outside the mutex
1050  self._call_cb('ec_event', (ec_handle, state))
1051 
1052  def _get_ec_state(self, ec):
1053  # Get the state of this component in an EC and return the enum value.
1054  if self._obj.is_alive(ec._obj):
1055  ec_state = ec.get_component_state(self._obj)
1056  if ec_state == RTC.ACTIVE_STATE:
1057  return self.ACTIVE
1058  elif ec_state == RTC.ERROR_STATE:
1059  return self.ERROR
1060  elif ec_state == RTC.INACTIVE_STATE:
1061  return self.INACTIVE
1062  else:
1063  return self.UNKNOWN
1064  else:
1065  return self.CREATED
1066 
1067  def _heartbeat(self):
1068  # Received a heart beat
1069  self._last_heartbeat = time.time()
1070  self._call_cb('heartbeat', self._last_heartbeat)
1071 
1073  # Parse the component's configuration sets
1074  with self._mutex:
1075  self._conf = self.object.get_configuration()
1076  self._conf_sets = {}
1077  for cs in self._conf.get_configuration_sets():
1078  self._conf_sets[cs.id] = ConfigurationSet(self, cs, cs.description,
1079  nvlist_to_dict(cs.configuration_data))
1080  try:
1081  self._active_conf_set = self._conf.get_active_configuration_set().id
1082  except SDOPackage.NotAvailable:
1083  self._active_conf_set = ''
1084 
1085  def _parse_profile(self):
1086  # Parse the component's profile
1087  with self._mutex:
1088  profile = self._obj.get_component_profile()
1089  self._instance_name = profile.instance_name
1090  self._type_name = profile.type_name
1091  self._description = profile.description
1092  self._version = profile.version
1093  self._vendor = profile.vendor
1094  self._category = profile.category
1095  if profile.parent:
1096  self._parent_obj = \
1097  profile.parent.get_component_profile().instance_name
1098  else:
1099  self._parent_obj = ''
1100  self._properties = nvlist_to_dict(profile.properties)
1101 
1102  def _port_event(self, port_name, event):
1103  def get_port_obj(port_name):
1104  for p_obj in self._obj.get_ports():
1105  prof = p_obj.get_port_profile()
1106  if prof.name == port_name:
1107  return p_obj
1108  raise ValueError(port_name)
1109 
1110  with self._mutex:
1111  if self._ports:
1112  if event == self.PORT_ADD:
1113  # New port
1114  p_obj = get_port_obj(port_name)
1115  self._ports.append(parse_port(p_obj, self))
1116  elif event == self.PORT_REMOVE:
1117  # Port removed
1118  p = self.get_port_by_name(port_name)
1119  self._ports.remove(p)
1120  elif event == self.PORT_CONNECT:
1121  # A port has a new connection
1122  p = self.get_port_by_name(port_name)
1123  p.reparse_connections()
1124  elif event == self.PORT_DISCONNECT:
1125  # A port has had a connection removed
1126  p = self.get_port_by_name(port_name)
1127  p.reparse_connections()
1128  # Call callbacks outside the mutex
1129  self._call_cb('port_event', (port_name, event))
1130 
1131  def _profile_update(self, items):
1132  # Reparse the profile
1133  self._parse_profile()
1134  # Call callbacks outside the mutex
1135  self._call_cb('component_profile', items)
1136 
1137  def _reset_conf_sets(self):
1138  with self._mutex:
1139  self._conf_sets = None
1140  self._active_conf_set = None
1141 
1142  def _reset_data(self):
1143  self._reset_owned_ecs()
1145  self._reset_ports()
1146  self._reset_conf_sets()
1147  self._reset_composite()
1148 
1149  def _reset_owned_ecs(self):
1150  with self._mutex:
1151  self._owned_ecs = None
1152  self._owned_ec_states = None
1153 
1155  with self._mutex:
1156  self._owned_ec_states = None
1157 
1159  with self._mutex:
1160  self._participating_ecs = None
1161  self._participating_ec_states = None
1162 
1164  with self._mutex:
1165  self._participating_ec_states = None
1166 
1167  def _reset_ports(self):
1168  with self._mutex:
1169  self._ports = None
1170 
1171  def _reset_composite(self):
1172  with self._mutex:
1173  self._orgs = []
1174  self._parent_orgs = []
1175  self._members = {}
1176 
1177  def _set_state_in_ec(self, ec_handle, state):
1178  # Forcefully set the state of this component in an EC
1179  with self._mutex:
1180  if ec_handle >= len(self.owned_ecs):
1181  ec_handle -= len(self.owned_ecs)
1182  if ec_handle >= len(self.participating_ecs):
1183  raise BadECIndexError(ec_handle)
1184  self.participating_ec_states[ec_handle] = state
1185  else:
1186  self.owned_ec_states[ec_handle] = state
1187  # Call callbacks outside the mutex
1188  self._call_cb('rtc_status', (ec_handle, state))
1189 
1190  # Constant for a component in the inactive state
1191  INACTIVE = 1
1192  # Constant for a component in the active state
1193  ACTIVE = 2
1194  # Constant for a component in the error state
1195  ERROR = 3
1196  # Constant for a component in an unknown state
1197  UNKNOWN = 4
1198  # Constant for a component in the created state
1199  CREATED = 5
1200 
1201  # Constant for execution context event "attached"
1202  EC_ATTACHED = 11
1203  # Constant for execution context event "detached"
1204  EC_DETACHED = 12
1205  # Constant for execution context event "rate_changed"
1206  EC_RATE_CHANGED = 13
1207  # Constant for execution context event "startup"
1208  EC_STARTUP = 14
1209  # Constant for execution context event "shutdown"
1210  EC_SHUTDOWN = 15
1211 
1212  # Constant for port event 'add'
1213  PORT_ADD = 21
1214  # Constant for port event 'remove'
1215  PORT_REMOVE = 22
1216  # Constant for port event 'connect'
1217  PORT_CONNECT = 23
1218  # Constant for port event 'disconnect'
1219  PORT_DISCONNECT = 24
1220 
1221  # Constant for configuration set event 'update_set'
1222  CFG_UPDATE_SET = 31
1223  # Constant for configuration set event 'update_param'
1224  CFG_UPDATE_PARAM = 32
1225  # Constant for configuration set event 'set_active_set'
1226  CFG_SET_SET = 33
1227  # Constant for configuration set event 'add_set'
1228  CFG_ADD_SET = 34
1229  # Constant for configuration set event 'remove_set'
1230  CFG_REMOVE_SET = 35
1231  # Constant for configuration set event 'activate_set'
1232  CFG_ACTIVATE_SET = 36
1233 
1234 
1235 # vim: tw=79
1236 
Execution context object.
Definition: exec_context.py:30
def get_ec(self, ec_handle)
Definition: component.py:423
def activate_conf_set(self, set_name)
Configuration set management.
Definition: component.py:887
def _add_child(self)
Internal API.
Definition: component.py:948
def deactivate_in_ec(self, ec_index)
Definition: component.py:391
def __init__(self, name=None, parent=None, obj=None, args, kwargs)
Definition: component.py:76
def set_conf_set_value(self, set_name, param, value)
Definition: component.py:898
def get_state_in_ec_string(self, ec_index, add_colour=True)
Definition: component.py:488
def participating_ec_states(self)
Definition: component.py:630
Component node object.
Definition: component.py:38
def activate_in_ec(self, ec_index)
Definition: component.py:369
def add_members(self, rtcs)
Composite component information and management.
Definition: component.py:193
def _profile_update(self, items)
Definition: component.py:1131
def refresh_state_in_ec(self, ec_index)
Definition: component.py:567
def add_logger(self, cb, level='NORMAL', filters='ALL')
Definition: component.py:838
def disconnect_all(self)
Port management.
Definition: component.py:708
def _reset_participating_ec_states(self)
Definition: component.py:1163
def has_port_by_ref(self, port_ref)
Definition: component.py:740
def name(self)
Definition: node.py:291
def state_in_ec(self, ec_index)
Definition: component.py:546
def category(self)
Component information.
Definition: component.py:140
def remove_logger(self, cb_id)
Definition: component.py:871
def exit(self)
State management.
Definition: component.py:357
Configuration set object.
Definition: config_set.py:26
def get_state_string(self, add_colour=True)
Definition: component.py:463
def _ec_event(self, ec_handle, event)
Definition: component.py:1008
def is_member(self, rtc)
Definition: component.py:255
def _reset_participating_ecs(self)
Definition: component.py:1158
def _config_event(self, name, event)
Definition: component.py:952
def remove_members(self, rtcs)
Definition: component.py:209
def reparse_participating_ecs(self)
Definition: component.py:124
def _port_event(self, port_name, event)
Definition: component.py:1102
def build_attr_string(attrs, supported=True)
Definition: utils.py:62
def dict_to_nvlist(dict)
Definition: utils.py:161
def get_port_by_name(self, port_name)
Definition: component.py:714
def heartbeat_time(self)
Node functionality.
Definition: component.py:814
def reset_in_ec(self, ec_index)
Definition: component.py:525
def _get_ec_state(self, ec)
Definition: component.py:1052
def _set_state_in_ec(self, ec_handle, state)
Definition: component.py:1177
def has_port_by_name(self, port_name)
Definition: component.py:733
Base node object.
Definition: node.py:29
def nvlist_to_dict(nvlist)
Definition: utils.py:169
def _enable_dynamic(self, enable=True)
Definition: component.py:981
def parse_port(port_obj, owner)
API functions.
Definition: ports.py:36
def _call_cb(self, event, value)
Definition: node.py:365
def _set_events(self, events)
Definition: node.py:380
def get_port_by_ref(self, port_ref)
Definition: component.py:722
def get_ec_index(self, ec_handle)
Definition: component.py:443


rtctree
Author(s): Geoffrey Biggs
autogenerated on Fri Jun 7 2019 21:56:24