00001
00002
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
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()
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
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
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
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
00229 if not self.is_member(rtc):
00230 raise NotInCompositionError(self.name, rtc_name)
00231
00232 org.remove_member(rtc_name)
00233
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
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
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
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
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
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
00947
00948 def _add_child(self):
00949
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
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
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
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
00974 del self._conf_sets[name]
00975 elif event == self.CFG_ACTIVATE_SET:
00976
00977 self._active_conf_set = name
00978
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
00999 self._last_heartbeat = time.time()
01000 else:
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
01029 self._participating_ecs.append(ExecutionContext(
01030 self._obj.get_context(ec_handle), ec_handle))
01031 elif event == self.EC_DETACHED:
01032
01033
01034
01035 ec, loc = get_ec(ec_handle)
01036 if ec:
01037 loc.remove(ec)
01038 elif event == self.EC_RATE_CHANGED:
01039
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
01050 self._call_cb('ec_event', (ec_handle, state))
01051
01052 def _get_ec_state(self, ec):
01053
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
01069 self._last_heartbeat = time.time()
01070 self._call_cb('heartbeat', self._last_heartbeat)
01071
01072 def _parse_configuration(self):
01073
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
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
01114 p_obj = get_port_obj(port_name)
01115 self._ports.append(parse_port(p_obj, self))
01116 elif event == self.PORT_REMOVE:
01117
01118 p = self.get_port_by_name(port_name)
01119 self._ports.remove(p)
01120 elif event == self.PORT_CONNECT:
01121
01122 p = self.get_port_by_name(port_name)
01123 p.reparse_connections()
01124 elif event == self.PORT_DISCONNECT:
01125
01126 p = self.get_port_by_name(port_name)
01127 p.reparse_connections()
01128
01129 self._call_cb('port_event', (port_name, event))
01130
01131 def _profile_update(self, items):
01132
01133 self._parse_profile()
01134
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
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
01188 self._call_cb('rtc_status', (ec_handle, state))
01189
01190
01191 INACTIVE = 1
01192
01193 ACTIVE = 2
01194
01195 ERROR = 3
01196
01197 UNKNOWN = 4
01198
01199 CREATED = 5
01200
01201
01202 EC_ATTACHED = 11
01203
01204 EC_DETACHED = 12
01205
01206 EC_RATE_CHANGED = 13
01207
01208 EC_STARTUP = 14
01209
01210 EC_SHUTDOWN = 15
01211
01212
01213 PORT_ADD = 21
01214
01215 PORT_REMOVE = 22
01216
01217 PORT_CONNECT = 23
01218
01219 PORT_DISCONNECT = 24
01220
01221
01222 CFG_UPDATE_SET = 31
01223
01224 CFG_UPDATE_PARAM = 32
01225
01226 CFG_SET_SET = 33
01227
01228 CFG_ADD_SET = 34
01229
01230 CFG_REMOVE_SET = 35
01231
01232 CFG_ACTIVATE_SET = 36
01233
01234
01235
01236