component.py
Go to the documentation of this file.
00001 # -*- Python -*-
00002 # -*- coding: utf-8 -*-
00003 
00004 '''rtctree
00005 
00006 Copyright (C) 2009-2014
00007     Geoffrey Biggs
00008     RT-Synthesis Research Group
00009     Intelligent Systems Research Institute,
00010     National Institute of Advanced Industrial Science and Technology (AIST),
00011     Japan
00012     All rights reserved.
00013 Licensed under the Eclipse Public License -v 1.0 (EPL)
00014 http://www.opensource.org/licenses/eclipse-1.0.txt
00015 
00016 Object representing a component node in the tree.
00017 
00018 '''
00019 
00020 
00021 import RTC
00022 import SDOPackage
00023 import time
00024 import uuid
00025 
00026 from rtctree.config_set import ConfigurationSet
00027 from rtctree.exceptions import *
00028 from rtctree.exec_context import ExecutionContext
00029 from rtctree.node import TreeNode
00030 import rtctree.sdo
00031 from rtctree.ports import parse_port
00032 from rtctree.utils import build_attr_string, nvlist_to_dict, dict_to_nvlist
00033 
00034 
00035 ##############################################################################
00036 ## Component node object
00037 
00038 class Component(TreeNode):
00039     '''Node representing a component on a name server.
00040 
00041     Component nodes can occur below name server and directory nodes. They
00042     cannot contain any children.
00043 
00044     Component nodes store all the properties of a component, as well as
00045     references to the actual objects and object types used in the component.
00046 
00047     This node can be made dynamic by setting the 'dynamic' property to True.
00048     The following callbacks are available on this node type when it is dynamic:
00049 
00050     - rtc_status(ec_handle, status)
00051       The component's state has changed in the specified execution context. The
00052       new status is one of Component.INACTIVE, Component.ACTIVE,
00053       Component.ERROR, Component.UNKNOWN and Component.CREATED.
00054     - component_profile(items)
00055       One or more members of the component's profiles have been updated. The
00056       updated items are listed in the "items" argument.
00057     - ec_event(ec_handle, event)
00058       A change in one of the attached execution contexts has occurred. The
00059       event is one of Component.EC_ATTACHED, Component.EC_DETACHED,
00060       Component.EC_RATE_CHANGED, Component.EC_STARTUP, and
00061       Component.EC_SHUTDOWN.
00062     - port_event(port_name, event)
00063       A change on the specified port has occurred. The event is one of
00064       Component.PORT_ADD, Component.PORT_REMOVE, Component.PORT_CONNECT and
00065       Component.PORT_DISCONNECT.
00066     - config_event(config_set_name, event)
00067       A change in the component's configuration sets has occurred. The event is
00068       one of Component.CFG_UPDATE_SET, Component.CFG_UPDATE_PARAM,
00069       Component.CFG_UPDATE_PARAM_IN_ACTIVE, Component.CFG_ADD_SET,
00070       Component.CFG_REMOVE_SET and Component.CFG_ACTIVATE_SET.
00071     - heartbeat(time)
00072       A heartbeat was received from the component. The time the beat was
00073       received is passed.
00074 
00075     '''
00076     def __init__(self, name=None, parent=None, obj=None, *args, **kwargs):
00077         '''Constructor.
00078 
00079         @param name Name of this component (i.e. its entry in the path).
00080         @param parent The parent node of this node, if any.
00081         @param obj The CORBA LightweightRTObject object to wrap.
00082 
00083         '''
00084         self._obj = obj
00085         self._obs = None
00086         self._obs_id = None
00087         self._loggers = {}
00088         self._last_heartbeat = time.time() # RTC is alive at construction time
00089         super(Component, self).__init__(name=name, parent=parent,
00090                                         *args, **kwargs)
00091         self._set_events(['rtc_status', 'component_profile', 'ec_event',
00092             'port_event', 'config_event', 'heartbeat'])
00093         self._reset_data()
00094         self._parse_profile()
00095 
00096     def reparse(self):
00097         '''Reparse the component's information.
00098 
00099         This will cause a delayed reparse of most information. This means that
00100         a piece of information, such as the list of ports, will be cleared and
00101         remain empty until it is next requested, at which point a fresh list
00102         will be retrieved from the component.
00103 
00104         If you only want to reparse a specific piece of information, use one of
00105         the reparse_X() methods.
00106 
00107         '''
00108         self._reset_data()
00109         self._parse_profile()
00110 
00111     def reparse_conf_sets(self):
00112         '''Delayed reparse configuration sets.'''
00113         self._reset_conf_sets()
00114 
00115     def reparse_ecs(self):
00116         '''Delayed reparse all execution contexts.'''
00117         self.reparse_owned_ecs()
00118         self.reparse_participating_ecs()
00119 
00120     def reparse_owned_ecs(self):
00121         '''Delayed reparse only owned execution contexts.'''
00122         self._reset_owned_ecs()
00123 
00124     def reparse_participating_ecs(self):
00125         '''Delayed reparse only participating execution contexts.'''
00126         self._reset_participating_ecs()
00127 
00128     def reparse_ports(self):
00129         '''Delayed reparse ports.'''
00130         self._reset_ports()
00131 
00132     def reparse_profile(self):
00133         '''Delayed reparse the component's profile.'''
00134         self._parse_profile()
00135 
00136     ###########################################################################
00137     # Component information
00138 
00139     @property
00140     def category(self):
00141         '''The category in which the component belongs.'''
00142         with self._mutex:
00143             return self._category
00144 
00145     @property
00146     def description(self):
00147         '''The component's description.'''
00148         with self._mutex:
00149             return self._description
00150 
00151     @property
00152     def instance_name(self):
00153         '''Instance name of the component.'''
00154         with self._mutex:
00155             return self._instance_name
00156 
00157     @property
00158     def parent_object(self):
00159         '''The name of the component's parent object (typically another
00160         component), if it has one.
00161 
00162         '''
00163         with self._mutex:
00164             return self._parent_obj
00165 
00166     @property
00167     def properties(self):
00168         '''The component's extra properties dictionary.'''
00169         with self._mutex:
00170             return self._properties
00171 
00172     @property
00173     def type_name(self):
00174         '''Type name of the component.'''
00175         with self._mutex:
00176             return self._type_name
00177 
00178     @property
00179     def vendor(self):
00180         '''The component's vendor.'''
00181         with self._mutex:
00182             return self._vendor
00183 
00184     @property
00185     def version(self):
00186         '''The component's version.'''
00187         with self._mutex:
00188             return self._version
00189 
00190     ###########################################################################
00191     # Composite component information and management
00192 
00193     def add_members(self, rtcs):
00194         '''Add other RT Components to this composite component as members.
00195 
00196         This component must be a composite component.
00197 
00198         '''
00199         if not self.is_composite:
00200             raise NotCompositeError(self.name)
00201         for rtc in rtcs:
00202             if self.is_member(rtc):
00203                 raise AlreadyInCompositionError(self.name, rtc.instance_name)
00204         org = self.organisations[0].obj
00205         org.add_members([x.object for x in rtcs])
00206         # Force a reparse of the member information
00207         self._orgs = []
00208 
00209     def remove_members(self, rtcs):
00210         '''Remove other RT Components from this composite component.
00211 
00212         rtcs is a list of components to remove. Each element must be either an
00213         rtctree.Component object or a string containing a component's instance
00214         name. rtctree.Component objects are more reliable.
00215 
00216         This component must be a composite component.
00217 
00218         '''
00219         if not self.is_composite:
00220             raise NotCompositeError(self.name)
00221         org = self.organisations[0].obj
00222         members = org.get_members()
00223         for rtc in rtcs:
00224             if type(rtc) == str:
00225                 rtc_name = rtc
00226             else:
00227                 rtc_name = rtc.instance_name
00228             # Check if the RTC actually is a member
00229             if not self.is_member(rtc):
00230                 raise NotInCompositionError(self.name, rtc_name)
00231             # Remove the RTC from the composition
00232             org.remove_member(rtc_name)
00233         # Force a reparse of the member information
00234         self._orgs = []
00235 
00236     @property
00237     def composite_parent(self):
00238         '''The parent component in the composition.
00239 
00240         None if this component is not a member of a composition.
00241 
00242         '''
00243         return None
00244 
00245     @property
00246     def is_composite(self):
00247         '''Is the component a composite component.'''
00248         return self._obj.get_owned_organizations() != []
00249 
00250     @property
00251     def is_composite_member(self):
00252         '''Is the component a member of a composite component.'''
00253         return self._obj.get_organizations() != []
00254 
00255     def is_member(self, rtc):
00256         '''Is the given component a member of this composition?
00257 
00258         rtc may be a Component object or a string containing a component's
00259         instance name. Component objects are more reliable.
00260 
00261         Returns False if the given component is not a member of this
00262         composition.
00263 
00264         Raises NotCompositeError if this component is not a composition.
00265 
00266         '''
00267         if not self.is_composite:
00268             raise NotCompositeError(self.name)
00269         members = self.organisations[0].obj.get_members()
00270         if type(rtc) is str:
00271             for m in members:
00272                 if m.get_component_profile().instance_name == rtc:
00273                     return True
00274         else:
00275             for m in members:
00276                 if m._is_equivalent(rtc.object):
00277                     return True
00278         return False
00279 
00280     @property
00281     def members(self):
00282         '''Member components if this component is composite.'''
00283         with self._mutex:
00284             if not self._members:
00285                 self._members = {}
00286                 for o in self.organisations:
00287                     # TODO: Search for these in the tree
00288                     self._members[o.org_id] = o.obj.get_members()
00289         return self._members
00290 
00291     @property
00292     def organisations(self):
00293         '''The organisations of this composition.'''
00294         class Org:
00295             def __init__(self, sdo_id, org_id, members, obj):
00296                 self.sdo_id = sdo_id
00297                 self.org_id = org_id
00298                 self.members = members
00299                 self.obj = obj
00300 
00301         with self._mutex:
00302             if not self._orgs:
00303                 for org in self._obj.get_owned_organizations():
00304                     owner = org.get_owner()
00305                     if owner:
00306                         sdo_id = owner._narrow(SDOPackage.SDO).get_sdo_id()
00307                     else:
00308                         sdo_id = ''
00309                     org_id = org.get_organization_id()
00310                     members = [m.get_sdo_id() for m in org.get_members()]
00311                     self._orgs.append(Org(sdo_id, org_id, members, org))
00312         return self._orgs
00313 
00314     @property
00315     def org_ids(self):
00316         '''The organisation IDs of this composition.'''
00317         return [sdo.get_organization_id() for sdo in \
00318                 self._obj.get_owned_organizations()]
00319 
00320     @property
00321     def parent_org_ids(self):
00322         '''The organisation IDs of the compositions this RTC belongs to.'''
00323         return [sdo.get_organization_id() for sdo in \
00324                 self._obj.get_organizations() if sdo]
00325 
00326     @property
00327     def parent_org_sdo_ids(self):
00328         '''The SDO IDs of the compositions this RTC belongs to.'''
00329         return [sdo.get_owner()._narrow(SDOPackage.SDO).get_sdo_id() \
00330                 for sdo in self._obj.get_organizations() if sdo]
00331 
00332     @property
00333     def parent_organisations(self):
00334         '''The organisations this RTC belongs to.'''
00335         class ParentOrg:
00336             def __init__(self, sdo_id, org_id):
00337                 self.sdo_id = sdo_id
00338                 self.org_id = org_id
00339 
00340         with self._mutex:
00341             if not self._parent_orgs:
00342                 for sdo in self._obj.get_organizations():
00343                     if not sdo:
00344                         continue
00345                     owner = sdo.get_owner()
00346                     if owner:
00347                         sdo_id = owner._narrow(SDOPackage.SDO).get_sdo_id()
00348                     else:
00349                         sdo_id = ''
00350                     org_id = sdo.get_organization_id()
00351                     self._parent_orgs.append(ParentOrg(sdo_id, org_id))
00352         return self._parent_orgs
00353 
00354     ###########################################################################
00355     # State management
00356 
00357     def exit(self):
00358         '''Stop the component's execution contexts and finalise it.
00359 
00360         This will have flow-on effects to any other components using this
00361         component's execution contexts and any child components.
00362 
00363         @return The result of attempting to exit.
00364 
00365         '''
00366         with self._mutex:
00367             return self._obj.exit()
00368 
00369     def activate_in_ec(self, ec_index):
00370         '''Activate this component in an execution context.
00371 
00372         @param ec_index The index of the execution context to activate in.
00373                         This index is into the total array of contexts, that
00374                         is both owned and participating contexts. If the value
00375                         of ec_index is greater than the length of
00376                         @ref owned_ecs, that length is subtracted from
00377                         ec_index and the result used as an index into
00378                         @ref participating_ecs.
00379 
00380         '''
00381         with self._mutex:
00382             if ec_index >= len(self.owned_ecs):
00383                 ec_index -= len(self.owned_ecs)
00384                 if ec_index >= len(self.participating_ecs):
00385                     raise BadECIndexError(ec_index)
00386                 ec = self.participating_ecs[ec_index]
00387             else:
00388                 ec = self.owned_ecs[ec_index]
00389             ec.activate_component(self._obj)
00390 
00391     def deactivate_in_ec(self, ec_index):
00392         '''Deactivate this component in an execution context.
00393 
00394         @param ec_index The index of the execution context to deactivate in.
00395                         This index is into the total array of contexts, that
00396                         is both owned and participating contexts. If the value
00397                         of ec_index is greater than the length of
00398                         @ref owned_ecs, that length is subtracted from
00399                         ec_index and the result used as an index into
00400                         @ref participating_ecs.
00401 
00402         '''
00403         with self._mutex:
00404             if ec_index >= len(self.owned_ecs):
00405                 ec_index -= len(self.owned_ecs)
00406                 if ec_index >= len(self.participating_ecs):
00407                     raise BadECIndexError(ec_index)
00408                 ec = self.participating_ecs[ec_index]
00409             else:
00410                 ec = self.owned_ecs[ec_index]
00411             ec.deactivate_component(self._obj)
00412 
00413     def exit(self):
00414         '''Make a component exit.
00415 
00416         This function will make the component exit, shutting down its CORBA
00417         object in the process. It will not remove the node from the tree; a
00418         reparse is necessary to do that.
00419 
00420         '''
00421         self._obj.exit()
00422 
00423     def get_ec(self, ec_handle):
00424         '''Get a reference to the execution context with the given handle.
00425 
00426         @param ec_handle The handle of the execution context to look for.
00427         @type ec_handle str
00428         @return A reference to the ExecutionContext object corresponding to
00429         the ec_handle.
00430         @raises NoECWithHandleError
00431 
00432         '''
00433         with self._mutex:
00434             for ec in self.owned_ecs:
00435                 if ec.handle == ec_handle:
00436                     return ec
00437             for ec in self.participating_ecs:
00438                 if ec.handle == ec_handle:
00439                     return ec
00440             raise NoECWithHandleError
00441 
00442 
00443     def get_ec_index(self, ec_handle):
00444         '''Get the index of the execution context with the given handle.
00445 
00446         @param ec_handle The handle of the execution context to look for.
00447         @type ec_handle str
00448         @return The index into the owned + participated arrays, suitable for
00449         use in methods such as @ref activate_in_ec, or -1 if the EC was not
00450         found.
00451         @raises NoECWithHandleError
00452 
00453         '''
00454         with self._mutex:
00455             for ii, ec in enumerate(self.owned_ecs):
00456                 if ec.handle == ec_handle:
00457                     return ii
00458             for ii, ec in enumerate(self.participating_ecs):
00459                 if ec.handle == ec_handle:
00460                     return ii + len(self.owned_ecs)
00461             raise NoECWithHandleError
00462 
00463     def get_state_string(self, add_colour=True):
00464         '''Get the state of this component as an optionally-coloured string.
00465 
00466         @param add_colour If True, ANSI colour codes will be added to the
00467                           string.
00468         @return A string describing the state of this component.
00469 
00470         '''
00471         with self._mutex:
00472             if self.state == self.INACTIVE:
00473                 result = 'Inactive', ['bold', 'blue']
00474             elif self.state == self.ACTIVE:
00475                 result = 'Active', ['bold', 'green']
00476             elif self.state == self.ERROR:
00477                 result = 'Error', ['bold', 'white', 'bgred']
00478             elif self.state == self.UNKNOWN:
00479                 result = 'Unknown', ['bold', 'red']
00480             elif self.state == self.CREATED:
00481                 result = 'Created', ['reset']
00482         if add_colour:
00483             return build_attr_string(result[1], supported=add_colour) + \
00484                     result[0] + build_attr_string('reset', supported=add_colour)
00485         else:
00486             return result[0]
00487 
00488     def get_state_in_ec_string(self, ec_index, add_colour=True):
00489         '''Get the state of the component in an execution context as a string.
00490 
00491         @param ec_index The index of the execution context to check the state
00492                         in. This index is into the total array of contexts,
00493                         that is both owned and participating contexts. If the
00494                         value of ec_index is greater than the length of @ref
00495                         owned_ecs, that length is subtracted from ec_index and
00496                         the result used as an index into @ref
00497                         participating_ecs.
00498 
00499         '''
00500         with self._mutex:
00501             if ec_index >= len(self.owned_ecs):
00502                 ec_index -= len(self.owned_ecs)
00503                 if ec_index >= len(self.participating_ecs):
00504                     raise BadECIndexError(ec_index)
00505                 state = self.participating_ec_states[ec_index]
00506             else:
00507                 state = self.owned_ec_states[ec_index]
00508         if state == self.INACTIVE:
00509             result = 'Inactive', ['bold', 'blue']
00510         elif state == self.ACTIVE:
00511             result = 'Active', ['bold', 'green']
00512         elif state == self.ERROR:
00513             result = 'Error', ['bold', 'white', 'bgred']
00514         elif state == self.UNKNOWN:
00515             result = 'Unknown', ['bold', 'red']
00516         elif state == self.CREATED:
00517             result = 'Created', ['reset']
00518         if add_colour:
00519             return build_attr_string(result[1], supported=add_colour) + \
00520                     result[0] + build_attr_string('reset',
00521                     supported=add_colour)
00522         else:
00523             return result[0]
00524 
00525     def reset_in_ec(self, ec_index):
00526         '''Reset this component in an execution context.
00527 
00528         @param ec_index The index of the execution context to reset in. This
00529                         index is into the total array of contexts, that is both
00530                         owned and participating contexts. If the value of
00531                         ec_index is greater than the length of @ref owned_ecs,
00532                         that length is subtracted from ec_index and the result
00533                         used as an index into @ref participating_ecs.
00534 
00535         '''
00536         with self._mutex:
00537             if ec_index >= len(self.owned_ecs):
00538                 ec_index -= len(self.owned_ecs)
00539                 if ec_index >= len(self.participating_ecs):
00540                     raise BadECIndexError(ec_index)
00541                 ec = self.participating_ecs[ec_index]
00542             else:
00543                 ec = self.owned_ecs[ec_index]
00544             ec.reset_component(self._obj)
00545 
00546     def state_in_ec(self, ec_index):
00547         '''Get the state of the component in an execution context.
00548 
00549         @param ec_index The index of the execution context to check the state
00550                         in. This index is into the total array of contexts,
00551                         that is both owned and participating contexts. If the
00552                         value of ec_index is greater than the length of @ref
00553                         owned_ecs, that length is subtracted from ec_index and
00554                         the result used as an index into @ref
00555                         participating_ecs.
00556 
00557         '''
00558         with self._mutex:
00559             if ec_index >= len(self.owned_ecs):
00560                 ec_index -= len(self.owned_ecs)
00561                 if ec_index >= len(self.participating_ecs):
00562                     raise BadECIndexError(ec_index)
00563                 return self.participating_ec_states[ec_index]
00564             else:
00565                 return self.owned_ec_states[ec_index]
00566 
00567     def refresh_state_in_ec(self, ec_index):
00568         '''Get the up-to-date state of the component in an execution context.
00569 
00570         This function will update the state, rather than using the cached
00571         value. This may take time, if the component is executing on a remote
00572         node.
00573 
00574         @param ec_index The index of the execution context to check the state
00575                         in. This index is into the total array of contexts,
00576                         that is both owned and participating contexts. If the
00577                         value of ec_index is greater than the length of @ref
00578                         owned_ecs, that length is subtracted from ec_index and
00579                         the result used as an index into @ref
00580                         participating_ecs.
00581 
00582         '''
00583         with self._mutex:
00584             if ec_index >= len(self.owned_ecs):
00585                 ec_index -= len(self.owned_ecs)
00586                 if ec_index >= len(self.participating_ecs):
00587                     raise BadECIndexError(ec_index)
00588                 state = self._get_ec_state(self.participating_ecs[ec_index])
00589                 self.participating_ec_states[ec_index] = state
00590             else:
00591                 state = self._get_ec_state(self.owned_ecs[ec_index])
00592                 self.owned_ec_states[ec_index] = state
00593             return state
00594 
00595     @property
00596     def alive(self):
00597         '''Is this component alive?'''
00598         with self._mutex:
00599             if self.exec_contexts:
00600                 for ec in self.exec_contexts:
00601                     if self._obj.is_alive(ec):
00602                         return True
00603         return False
00604 
00605     @property
00606     def owned_ec_states(self):
00607         '''The state of each execution context this component owns.'''
00608         with self._mutex:
00609             if not self._owned_ec_states:
00610                 if self.owned_ecs:
00611                     states = []
00612                     for ec in self.owned_ecs:
00613                         states.append(self._get_ec_state(ec))
00614                     self._owned_ec_states = states
00615                 else:
00616                     self._owned_ec_states = []
00617         return self._owned_ec_states
00618 
00619     @property
00620     def owned_ecs(self):
00621         '''A list of the execution contexts owned by this component.'''
00622         with self._mutex:
00623             if not self._owned_ecs:
00624                 self._owned_ecs = [ExecutionContext(ec,
00625                     self._obj.get_context_handle(ec)) \
00626                     for ec in self._obj.get_owned_contexts()]
00627         return self._owned_ecs
00628 
00629     @property
00630     def participating_ec_states(self):
00631         '''The state of each execution context this component is participating
00632         in.
00633 
00634         '''
00635         with self._mutex:
00636             if not self._participating_ec_states:
00637                 if self.participating_ecs:
00638                     states = []
00639                     for ec in self.participating_ecs:
00640                         states.append(self._get_ec_state(ec))
00641                     self._participating_ec_states = states
00642                 else:
00643                     self._participating_ec_states = []
00644         return self._participating_ec_states
00645 
00646     @property
00647     def participating_ecs(self):
00648         '''A list of the execution contexts this component is participating in.
00649 
00650         '''
00651         with self._mutex:
00652             if not self._participating_ecs:
00653                 self._participating_ecs = [ExecutionContext(ec,
00654                                     self._obj.get_context_handle(ec)) \
00655                              for ec in self._obj.get_participating_contexts()]
00656         return self._participating_ecs
00657 
00658     @property
00659     def plain_state_string(self):
00660         '''The state of this component as a string without colour.'''
00661         return self.get_state_string(add_colour=False)
00662 
00663     @property
00664     def state(self):
00665         '''The merged state of all the execution context states, which can be
00666         used as the overall state of this component.
00667 
00668         The order of precedence is:
00669             Error > Active > Inactive > Created > Unknown
00670 
00671         '''
00672         def merge_state(current, new):
00673             if new == self.ERROR:
00674                 return self.ERROR
00675             elif new == self.ACTIVE and current != self.ERROR:
00676                 return self.ACTIVE
00677             elif new == self.INACTIVE and \
00678                     current not in [self.ACTIVE, self.ERROR]:
00679                 return self.INACTIVE
00680             elif new == self.CREATED and \
00681                     current not in [self.ACTIVE, self.ERROR, self.INACTIVE]:
00682                 return self.CREATED
00683             elif current not in [self.ACTIVE, self.ERROR, self.INACTIVE,
00684                                  self.CREATED]:
00685                 return self.UNKNOWN
00686             return current
00687 
00688         with self._mutex:
00689             if not self.owned_ec_states and not self.participating_ec_states:
00690                 return self.UNKNOWN
00691             merged_state = self.CREATED
00692             if self.owned_ec_states:
00693                 for ec_state in self.owned_ec_states:
00694                     merged_state = merge_state(merged_state, ec_state)
00695             if self.participating_ec_states:
00696                 for ec_state in self.participating_ec_states:
00697                     merged_state = merge_state(merged_state, ec_state)
00698             return merged_state
00699 
00700     @property
00701     def state_string(self):
00702         '''The state of this component as a colourised string.'''
00703         return self.get_state_string()
00704 
00705     ###########################################################################
00706     # Port management
00707 
00708     def disconnect_all(self):
00709         '''Disconnect all connections to all ports of this component.'''
00710         with self._mutex:
00711             for p in self.ports:
00712                 p.disconnect_all()
00713 
00714     def get_port_by_name(self, port_name):
00715         '''Get a port of this component by name.'''
00716         with self._mutex:
00717             for p in self.ports:
00718                 if p.name == port_name:
00719                     return p
00720             return None
00721 
00722     def get_port_by_ref(self, port_ref):
00723         '''Get a port of this component by reference to a CORBA PortService
00724         object.
00725 
00726         '''
00727         with self._mutex:
00728             for p in self.ports:
00729                 if p.object._is_equivalent(port_ref):
00730                     return p
00731             return None
00732 
00733     def has_port_by_name(self, port_name):
00734         '''Check if this component has a port by the given name.'''
00735         with self._mutex:
00736             if self.get_port_by_name(port_name):
00737                 return True
00738             return False
00739 
00740     def has_port_by_ref(self, port_ref):
00741         '''Check if this component has a port by the given reference to a CORBA
00742         PortService object.
00743 
00744         '''
00745         with self._mutex:
00746             if self.get_port_by_ref(self, port_ref):
00747                 return True
00748             return False
00749 
00750     @property
00751     def connected_inports(self):
00752         '''The list of all input ports belonging to this component that are
00753         connected to one or more other ports.
00754 
00755         '''
00756         return [p for p in self.ports \
00757                 if p.__class__.__name__ == 'DataInPort' and p.is_connected]
00758 
00759     @property
00760     def connected_outports(self):
00761         '''The list of all output ports belonging to this component that are
00762         connected to one or more other ports.
00763 
00764         '''
00765         return [p for p in self.ports \
00766                     if p.__class__.__name__ == 'DataOutPort' \
00767                     and p.is_connected]
00768 
00769     @property
00770     def connected_ports(self):
00771         '''The list of all ports belonging to this component that are connected
00772         to one or more other ports.
00773 
00774         '''
00775         return [p for p in self.ports if p.is_connected]
00776 
00777     @property
00778     def connected_svcports(self):
00779         '''The list of all service ports belonging to this component that are
00780         connected to one or more other ports.
00781 
00782         '''
00783         return [p for p in self.ports \
00784                 if p.__class__.__name__ == 'CorbaPort' and p.is_connected]
00785 
00786     @property
00787     def inports(self):
00788         '''The list of all input ports belonging to this component.'''
00789         return [p for p in self.ports if p.__class__.__name__ == 'DataInPort']
00790 
00791     @property
00792     def outports(self):
00793         '''The list of all output ports belonging to this component.'''
00794         return [p for p in self.ports if p.__class__.__name__ == 'DataOutPort']
00795 
00796     @property
00797     def ports(self):
00798         '''The list of all ports belonging to this component.'''
00799         with self._mutex:
00800             if not self._ports:
00801                 self._ports = [parse_port(port, self) \
00802                                for port in self._obj.get_ports()]
00803         return self._ports
00804 
00805     @property
00806     def svcports(self):
00807         '''The list of all service ports belonging to this component.'''
00808         return [p for p in self.ports if p.__class__.__name__ == 'CorbaPort']
00809 
00810     ###########################################################################
00811     # Node functionality
00812 
00813     @property
00814     def heartbeat_time(self):
00815         '''The time of the last heartbeat.
00816 
00817         Updated only when the node is dynamic.
00818 
00819         '''
00820         return self._last_heartbeat
00821 
00822     @property
00823     def is_component(self):
00824         '''Is this node a component?'''
00825         return True
00826 
00827     @property
00828     def loggers(self):
00829         '''Returns the list of logger IDs attached to this component.'''
00830         return self._loggers.keys()
00831 
00832     @property
00833     def object(self):
00834         '''The LightweightRTObject this object wraps.'''
00835         with self._mutex:
00836             return self._obj
00837 
00838     def add_logger(self, cb, level='NORMAL', filters='ALL'):
00839         '''Add a callback to receive log events from this component.
00840 
00841         @param cb The callback function to receive log events. It must have the
00842             signature cb(name, time, source, level, message), where name is the
00843             name of the component the log record came from, time is a
00844             floating-point time stamp, source is the name of the logger that
00845             provided the log record, level is the log level of the record and
00846             message is a text string.
00847         @param level The maximum level of log records to receive.
00848         @param filters Filter the objects from which to receive log messages.
00849         @return An ID for this logger. Use this ID in future operations such as
00850                 removing this logger.
00851         @raises AddLoggerError
00852 
00853         '''
00854         with self._mutex:
00855             obs = rtctree.sdo.RTCLogger(self, cb)
00856             uuid_val = uuid.uuid4()
00857             intf_type = obs._this()._NP_RepositoryId
00858             props = {'logger.log_level': level,
00859                     'logger.filter': filters}
00860             props = dict_to_nvlist(props)
00861             sprof = SDOPackage.ServiceProfile(id=uuid_val.get_bytes(),
00862                     interface_type=intf_type, service=obs._this(),
00863                     properties=props)
00864             conf = self.object.get_configuration()
00865             res = conf.add_service_profile(sprof)
00866             if res:
00867                 self._loggers[uuid_val] = obs
00868                 return uuid_val
00869             raise AddLoggerError(self.name)
00870 
00871     def remove_logger(self, cb_id):
00872         '''Remove a logger.
00873 
00874         @param cb_id The ID of the logger to remove.
00875         @raises NoLoggerError
00876 
00877         '''
00878         if cb_id not in self._loggers:
00879             raise NoLoggerError(cb_id, self.name)
00880         conf = self.object.get_configuration()
00881         res = conf.remove_service_profile(cb_id.get_bytes())
00882         del self._loggers[cb_id]
00883 
00884     ###########################################################################
00885     # Configuration set management
00886 
00887     def activate_conf_set(self, set_name):
00888         '''Activate a configuration set by name.
00889 
00890         @raises NoSuchConfSetError
00891 
00892         '''
00893         with self._mutex:
00894             if not set_name in self.conf_sets:
00895                 raise NoSuchConfSetError(set_name)
00896             self._conf.activate_configuration_set(set_name)
00897 
00898     def set_conf_set_value(self, set_name, param, value):
00899         '''Set a configuration set parameter value.
00900 
00901         @param set_name The name of the configuration set the destination
00902                         parameter is in.
00903         @param param The name of the parameter to set.
00904         @param value The new value for the parameter.
00905         @raises NoSuchConfSetError, NoSuchConfParamError
00906 
00907         '''
00908         with self._mutex:
00909             if not set_name in self.conf_sets:
00910                 raise NoSuchConfSetError(set_name)
00911             if not self.conf_sets[set_name].has_param(param):
00912                 raise NoSuchConfParamError(param)
00913             self.conf_sets[set_name].set_param(param, value)
00914             self._conf.set_configuration_set_values(\
00915                     self.conf_sets[set_name].object)
00916 
00917     @property
00918     def active_conf_set(self):
00919         '''The currently-active configuration set.'''
00920         with self._mutex:
00921             if not self.conf_sets:
00922                 return None
00923             if not self._active_conf_set:
00924                 return None
00925             return self.conf_sets[self._active_conf_set]
00926 
00927     @property
00928     def active_conf_set_name(self):
00929         '''The name of the currently-active configuration set.'''
00930         with self._mutex:
00931             if not self.conf_sets:
00932                 return ''
00933             if not self._active_conf_set:
00934                 return ''
00935             return self._active_conf_set
00936 
00937     @property
00938     def conf_sets(self):
00939         '''The dictionary of configuration sets in this component, if any.'''
00940         with self._mutex:
00941             if not self._conf_sets:
00942                 self._parse_configuration()
00943         return self._conf_sets
00944 
00945     ###########################################################################
00946     # Internal API
00947 
00948     def _add_child(self):
00949         # Components cannot contain children.
00950         raise CannotHoldChildrenError
00951 
00952     def _config_event(self, name, event):
00953         with self._mutex:
00954             if self._conf_sets:
00955                 if event == self.CFG_UPDATE_SET:
00956                     # A configuration set has been updated
00957                     cs = self._conf.get_configuration_set(name)
00958                     self._conf_sets[name]._reload(cs, cs.description,
00959                             nvlist_to_dict(cs.configuration_data))
00960                 elif event == self.CFG_UPDATE_PARAM:
00961                     # A parameter in a configuration set has been changed
00962                     cset, param = name.split('.')
00963                     cs = self._conf.get_configuration_set(cset)
00964                     data = nvlist_to_dict(cs.configuration_data)
00965                     self._conf_sets[cset].set_param(param, data[param])
00966                 elif event == self.CFG_ADD_SET:
00967                     # A new configuration set has been added
00968                     cs = self._conf.get_configuration_set(name)
00969                     self._conf_sets[name] = ConfigurationSet(self, cs,
00970                             cs.description,
00971                             nvlist_to_dict(cs.configuration_data))
00972                 elif event == self.CFG_REMOVE_SET:
00973                     # Remove the configuration set
00974                     del self._conf_sets[name]
00975                 elif event == self.CFG_ACTIVATE_SET:
00976                     # Change the active configuration set
00977                     self._active_conf_set = name
00978         # Call callbacks outside the mutex
00979         self._call_cb('config_event', (name, event))
00980 
00981     def _enable_dynamic(self, enable=True):
00982         if enable:
00983             obs = rtctree.sdo.RTCObserver(self)
00984             uuid_val = uuid.uuid4().get_bytes()
00985             intf_type = obs._this()._NP_RepositoryId
00986             props = dict_to_nvlist({'heartbeat.enable': 'YES',
00987                 'heartbeat.interval': '1.0',
00988                 'observed_status': 'ALL'})
00989             sprof = SDOPackage.ServiceProfile(id=uuid_val,
00990                     interface_type=intf_type, service=obs._this(),
00991                     properties=props)
00992             conf = self.object.get_configuration()
00993             res = conf.add_service_profile(sprof)
00994             if res:
00995                 self._dynamic = True
00996                 self._obs = obs
00997                 self._obs_id = uuid_val
00998                 # If we could set an observer, the component is alive
00999                 self._last_heartbeat = time.time()
01000         else: # Disable
01001             conf = self.object.get_configuration()
01002             res = conf.remove_service_profile(self._obs_id)
01003             if res:
01004                 self._dynamic = False
01005                 self._obs = None
01006                 self._obs_id = None
01007 
01008     def _ec_event(self, ec_handle, event):
01009         def get_ec(ec_handle):
01010             tgt_ec = None
01011             loc = None
01012             if self._owned_ecs:
01013                 for ec in self._owned_ecs:
01014                     if ec.handle == ec_handle:
01015                         tgt_ec = ec
01016                         loc = self._owned_ecs
01017                         break
01018             if not del_ec and self._participating_ecs:
01019                 for ec in self._participating_ecs:
01020                     if ec.handle == ec_handle:
01021                         tgt_ec = ec
01022                         loc = self._participating_ecs
01023                         break
01024             return tgt_ec, loc
01025 
01026         with self._mutex:
01027             if event == self.EC_ATTACHED:
01028                 # New EC has been attached
01029                 self._participating_ecs.append(ExecutionContext(
01030                     self._obj.get_context(ec_handle), ec_handle))
01031             elif event == self.EC_DETACHED:
01032                 # An EC has been detached; delete the local facade
01033                 # if ec is not None, the corresponding EC has a local
01034                 # facade that needs deleting
01035                 ec, loc = get_ec(ec_handle)
01036                 if ec:
01037                     loc.remove(ec)
01038             elif event == self.EC_RATE_CHANGED:
01039                 # Nothing to do
01040                 pass
01041             elif event == self.EC_STARTUP:
01042                 ec, loc = get_ec(ec_handle)
01043                 if ec:
01044                     ec._set_running(True)
01045             elif event == self.EC_SHUTDOWN:
01046                 ec, loc = get_ec(ec_handle)
01047                 if ec:
01048                     ec._set_running(False)
01049         # Call callbacks outside the mutex
01050         self._call_cb('ec_event', (ec_handle, state))
01051 
01052     def _get_ec_state(self, ec):
01053         # Get the state of this component in an EC and return the enum value.
01054         if self._obj.is_alive(ec._obj):
01055             ec_state = ec.get_component_state(self._obj)
01056             if ec_state == RTC.ACTIVE_STATE:
01057                 return self.ACTIVE
01058             elif ec_state == RTC.ERROR_STATE:
01059                 return self.ERROR
01060             elif ec_state == RTC.INACTIVE_STATE:
01061                 return self.INACTIVE
01062             else:
01063                 return self.UNKNOWN
01064         else:
01065             return self.CREATED
01066 
01067     def _heartbeat(self):
01068         # Received a heart beat
01069         self._last_heartbeat = time.time()
01070         self._call_cb('heartbeat', self._last_heartbeat)
01071 
01072     def _parse_configuration(self):
01073         # Parse the component's configuration sets
01074         with self._mutex:
01075             self._conf = self.object.get_configuration()
01076             self._conf_sets = {}
01077             for cs in self._conf.get_configuration_sets():
01078                 self._conf_sets[cs.id] = ConfigurationSet(self, cs, cs.description,
01079                         nvlist_to_dict(cs.configuration_data))
01080             try:
01081                 self._active_conf_set = self._conf.get_active_configuration_set().id
01082             except SDOPackage.NotAvailable:
01083                 self._active_conf_set = ''
01084 
01085     def _parse_profile(self):
01086         # Parse the component's profile
01087         with self._mutex:
01088             profile = self._obj.get_component_profile()
01089             self._instance_name = profile.instance_name
01090             self._type_name = profile.type_name
01091             self._description = profile.description
01092             self._version = profile.version
01093             self._vendor = profile.vendor
01094             self._category = profile.category
01095             if profile.parent:
01096                 self._parent_obj = \
01097                         profile.parent.get_component_profile().instance_name
01098             else:
01099                 self._parent_obj = ''
01100             self._properties = nvlist_to_dict(profile.properties)
01101 
01102     def _port_event(self, port_name, event):
01103         def get_port_obj(port_name):
01104             for p_obj in self._obj.get_ports():
01105                 prof = p_obj.get_port_profile()
01106                 if prof.name == port_name:
01107                     return p_obj
01108             raise ValueError(port_name)
01109 
01110         with self._mutex:
01111             if self._ports:
01112                 if event == self.PORT_ADD:
01113                     # New port
01114                     p_obj = get_port_obj(port_name)
01115                     self._ports.append(parse_port(p_obj, self))
01116                 elif event == self.PORT_REMOVE:
01117                     # Port removed
01118                     p = self.get_port_by_name(port_name)
01119                     self._ports.remove(p)
01120                 elif event == self.PORT_CONNECT:
01121                     # A port has a new connection
01122                     p = self.get_port_by_name(port_name)
01123                     p.reparse_connections()
01124                 elif event == self.PORT_DISCONNECT:
01125                     # A port has had a connection removed
01126                     p = self.get_port_by_name(port_name)
01127                     p.reparse_connections()
01128         # Call callbacks outside the mutex
01129         self._call_cb('port_event', (port_name, event))
01130 
01131     def _profile_update(self, items):
01132         # Reparse the profile
01133         self._parse_profile()
01134         # Call callbacks outside the mutex
01135         self._call_cb('component_profile', items)
01136 
01137     def _reset_conf_sets(self):
01138         with self._mutex:
01139             self._conf_sets = None
01140             self._active_conf_set = None
01141 
01142     def _reset_data(self):
01143         self._reset_owned_ecs()
01144         self._reset_participating_ecs()
01145         self._reset_ports()
01146         self._reset_conf_sets()
01147         self._reset_composite()
01148 
01149     def _reset_owned_ecs(self):
01150         with self._mutex:
01151             self._owned_ecs = None
01152             self._owned_ec_states = None
01153 
01154     def _reset_owned_ec_states(self):
01155         with self._mutex:
01156             self._owned_ec_states = None
01157 
01158     def _reset_participating_ecs(self):
01159         with self._mutex:
01160             self._participating_ecs = None
01161             self._participating_ec_states = None
01162 
01163     def _reset_participating_ec_states(self):
01164         with self._mutex:
01165             self._participating_ec_states = None
01166 
01167     def _reset_ports(self):
01168         with self._mutex:
01169             self._ports = None
01170 
01171     def _reset_composite(self):
01172         with self._mutex:
01173             self._orgs = []
01174             self._parent_orgs = []
01175             self._members = {}
01176 
01177     def _set_state_in_ec(self, ec_handle, state):
01178         # Forcefully set the state of this component in an EC
01179         with self._mutex:
01180             if ec_handle >= len(self.owned_ecs):
01181                 ec_handle -= len(self.owned_ecs)
01182                 if ec_handle >= len(self.participating_ecs):
01183                     raise BadECIndexError(ec_handle)
01184                 self.participating_ec_states[ec_handle] = state
01185             else:
01186                 self.owned_ec_states[ec_handle] = state
01187         # Call callbacks outside the mutex
01188         self._call_cb('rtc_status', (ec_handle, state))
01189 
01190     # Constant for a component in the inactive state
01191     INACTIVE = 1
01192     # Constant for a component in the active state
01193     ACTIVE = 2
01194     # Constant for a component in the error state
01195     ERROR = 3
01196     # Constant for a component in an unknown state
01197     UNKNOWN = 4
01198     # Constant for a component in the created state
01199     CREATED = 5
01200 
01201     # Constant for execution context event "attached"
01202     EC_ATTACHED = 11
01203     # Constant for execution context event "detached"
01204     EC_DETACHED = 12
01205     # Constant for execution context event "rate_changed"
01206     EC_RATE_CHANGED = 13
01207     # Constant for execution context event "startup"
01208     EC_STARTUP = 14
01209     # Constant for execution context event "shutdown"
01210     EC_SHUTDOWN = 15
01211 
01212     # Constant for port event 'add'
01213     PORT_ADD = 21
01214     # Constant for port event 'remove'
01215     PORT_REMOVE = 22
01216     # Constant for port event 'connect'
01217     PORT_CONNECT = 23
01218     # Constant for port event 'disconnect'
01219     PORT_DISCONNECT = 24
01220 
01221     # Constant for configuration set event 'update_set'
01222     CFG_UPDATE_SET = 31
01223     # Constant for configuration set event 'update_param'
01224     CFG_UPDATE_PARAM = 32
01225     # Constant for configuration set event 'set_active_set'
01226     CFG_SET_SET = 33
01227     # Constant for configuration set event 'add_set'
01228     CFG_ADD_SET = 34
01229     # Constant for configuration set event 'remove_set'
01230     CFG_REMOVE_SET = 35
01231     # Constant for configuration set event 'activate_set'
01232     CFG_ACTIVATE_SET = 36
01233 
01234 
01235 # vim: tw=79
01236 


rtctree
Author(s): Geoffrey Biggs
autogenerated on Wed Aug 26 2015 16:13:08