00001
00002
00003
00004 '''rtsprofile
00005
00006 Copyright (C) 2009-2010
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 File: component.py
00017
00018 Object representing a component in an RT system.
00019
00020 '''
00021
00022 __version__ = '$Revision: $'
00023
00024
00025
00026 from rtsprofile import RTS_NS, RTS_NS_S, RTS_EXT_NS, RTS_EXT_NS_S, \
00027 RTS_EXT_NS_YAML
00028 from rtsprofile import composite_type as comp_type
00029 from rtsprofile.config_set import ConfigurationSet
00030 from rtsprofile.exceptions import InvalidCompositeTypeError
00031 from rtsprofile.exec_context import ExecutionContext
00032 from rtsprofile.location import Location
00033 from rtsprofile.ports import DataPort, ServicePort
00034 from rtsprofile.utils import get_direct_child_elements_xml, \
00035 indent_string, parse_properties_xml, \
00036 properties_to_xml, validate_attribute
00037
00038
00039
00040
00041
00042 class Component(object):
00043 '''Information about a component contained in an RT system.'''
00044
00045 def __init__(self, id='', path_uri='', active_configuration_set='',
00046 instance_name='', composite_type=comp_type.NONE,
00047 is_required=False, comment='', visible=True,
00048 location=Location()):
00049 '''@param id Component ID.
00050 @type id str
00051 @param path_uri Path to the component.
00052 @type path_uri str
00053 @param active_configuration_set Name of the active configuration set.
00054 @type active_configuration_set str
00055 @param instance_name Component's instance name.
00056 @type instance_name str
00057 @param composite_type Type of composition the component is in.
00058 @type composite_type CompositeType
00059 @param is_required If the component is optional in the system.
00060 @type is_required bool
00061 @param comment A comment about the component.
00062 @type comment str
00063 @param visible If this component is visible in graphical displays.
00064 @type visible bool
00065 @param location The location of this component in graphical displays.
00066 @type location Location
00067
00068 '''
00069 self._reset()
00070 validate_attribute(id, 'component.id',
00071 expected_type=[str, unicode], required=False)
00072 self._id = id
00073 validate_attribute(path_uri, 'component.pathUri',
00074 expected_type=[str, unicode], required=False)
00075 self._path_uri = path_uri
00076 validate_attribute(active_configuration_set,
00077 'component.activeConfigurationSet',
00078 expected_type=[str, unicode], required=False)
00079 self._active_config_set = active_configuration_set
00080 validate_attribute(instance_name, 'component.instanceName',
00081 expected_type=[str, unicode], required=False)
00082 self._instance_name = instance_name
00083 validate_attribute(composite_type, 'component.compositeType',
00084 expected_type=comp_type.const_type, required=False)
00085 self._composite_type = composite_type
00086 validate_attribute(is_required, 'component.isRequired',
00087 expected_type=bool)
00088 self._is_required = is_required
00089 validate_attribute(comment, 'component.ext.comment',
00090 expected_type=[str, unicode], required=False)
00091 self._comment = comment
00092 validate_attribute(visible, 'component.ext.visible',
00093 expected_type=bool, required=False)
00094 self._visible = visible
00095 validate_attribute(location, 'component.ext.Location',
00096 expected_type=Location, required=True)
00097 self._location = location
00098
00099 def __str__(self):
00100 result = 'Instance name: {3}\n ID: {0}\n Path URI: {1}\n Active \
00101 configuration set: {2}\n Composite type: {4}\n Is required: {5}\n'.format(\
00102 self.id, self.path_uri, self.active_configuration_set,
00103 self.instance_name, comp_type.to_string(self.composite_type),
00104 self.is_required)
00105 if self.comment:
00106 result += ' Comment: {0}\n'.format(self.comment)
00107 result += ' Visible: {0}\n'.format(self.visible)
00108 if self.data_ports:
00109 result += ' Data ports:\n'
00110 for p in self.data_ports:
00111 result += '{0}\n'.format(indent_string(str(p), num_spaces=4))
00112 if self.service_ports:
00113 result += ' Service ports:\n'
00114 for p in self.service_ports:
00115 result += '{0}\n'.format(indent_string(str(p), num_spaces=4))
00116 if self.configuration_sets:
00117 result += ' Configuration sets:\n'
00118 for c in self.configuration_sets:
00119 result += '{0}\n'.format(indent_string(str(c), num_spaces=4))
00120 if self.execution_contexts:
00121 result += ' Execution contexts:\n'
00122 for e in self.execution_contexts:
00123 result += '{0}\n'.format(indent_string(str(e), num_spaces=4))
00124 if self.participants:
00125 result += ' Participants:\n'
00126 for p in self.participants:
00127 result += '{0}\n'.format(indent_string(str(p)))
00128 result += ' Location:\n{0}\n'.format(indent_string(str(self.location),
00129 num_spaces=4))
00130 if self.properties:
00131 result += ' Properties:\n'
00132 for p in self.properties:
00133 result += ' {0}: {1}\n'.format(p, self.properties[p])
00134 return result[:-1]
00135
00136
00137
00138
00139 @property
00140 def id(self):
00141 '''ID of this component in the RT system.
00142
00143 In case of the same RT Component specification being used to create
00144 multiple RT Components within a single RT system, this ID is prepended
00145 to the instance name attribute to distinguish individual components.
00146
00147 '''
00148 return self._id
00149
00150 @id.setter
00151 def id(self, id):
00152 validate_attribute(id, 'component.id',
00153 expected_type=[str, unicode], required=True)
00154 self._id = id
00155
00156 @property
00157 def path_uri(self):
00158 '''Path to where this component is registered in URI format.'''
00159 return self._path_uri
00160
00161 @path_uri.setter
00162 def path_uri(self, path_uri):
00163 validate_attribute(path_uri, 'component.pathUri',
00164 expected_type=[str, unicode], required=True)
00165 self._path_uri = path_uri
00166
00167 @property
00168 def active_configuration_set(self):
00169 '''The ID of the active configuration set of the component.
00170
00171 If no configuration set is active, this may be empty.
00172
00173 '''
00174 return self._active_config_set
00175
00176 @active_configuration_set.setter
00177 def active_configuration_set(self, active_config_set):
00178 validate_attribute(active_config_set,
00179 'component.activeConfigurationSet',
00180 expected_type=[str, unicode], required=False)
00181 self._active_config_set = active_config_set
00182
00183 @property
00184 def instance_name(self):
00185 '''Instance name of the component in the RT system.
00186
00187 In case of the same RT Component specification being used to create
00188 multiple RT Components within a single RT system, this instance name is
00189 appended to the ID attribute to distinguish individual components.
00190
00191 '''
00192 return self._instance_name
00193
00194 @instance_name.setter
00195 def instance_name(self, instance_name):
00196 validate_attribute(instance_name, 'component.instanceName',
00197 expected_type=[str, unicode], required=True)
00198 self._instance_name = instance_name
00199
00200 @property
00201 def composite_type(self):
00202 '''The type of composite component this component is involved in.
00203
00204 If this component is involved in a composite component, this attribute
00205 specifies the type of composition. See @ref CompositeType for valid
00206 values.
00207
00208 '''
00209 return self._composite_type
00210
00211 @composite_type.setter
00212 def composite_type(self, composite_type):
00213 validate_attribute(composite_type, 'component.compositeType',
00214 expected_type=comp_type.const_type, required=True)
00215 self._composite_type = composite_type
00216
00217 @property
00218 def is_required(self):
00219 '''Specifies if this component is optional in the RT system.
00220
00221 Sometimes a component does not need to be present for an RT system to
00222 function. If this component must be present for the RT system to
00223 function, this attribute will be True.
00224
00225 '''
00226 return self._is_required
00227
00228 @is_required.setter
00229 def is_required(self, is_required):
00230 validate_attribute(is_required, 'component.isRequired',
00231 expected_type=bool)
00232 self._is_required = is_required
00233
00234 @property
00235 def data_ports(self):
00236 '''Data ports owned by this component.
00237
00238 May be an empty list if this component has no data ports. Members are
00239 of type @ref DataPort.
00240
00241 '''
00242 return self._data_ports
00243
00244 @data_ports.setter
00245 def data_ports(self, data_ports):
00246 validate_attribute(data_ports, 'component.DataPorts',
00247 expected_type=list, required=False)
00248 self._data_ports = data_ports
00249
00250 @property
00251 def service_ports(self):
00252 '''Service ports owned by this component.
00253
00254 May be an empty list if this component has no service ports. Members
00255 are of type @ref ServicePort.
00256
00257 '''
00258 return self._service_ports
00259
00260 @service_ports.setter
00261 def service_ports(self, service_ports):
00262 validate_attribute(service_ports, 'component.ServicePorts',
00263 expected_type=list, required=False)
00264 self._service_ports = service_ports
00265
00266 @property
00267 def configuration_sets(self):
00268 '''The configuration sets in this component.
00269
00270 May be an empty list if this component has no configuration sets.
00271 Members are of type @ref ConfigurationSet.
00272
00273 '''
00274 return self._config_sets
00275
00276 @configuration_sets.setter
00277 def configuration_sets(self, configuration_sets):
00278 validate_attribute(configuration_sets, 'component.ConfigurationSets',
00279 expected_type=list, required=False)
00280 self._config_sets = configuration_sets
00281
00282 @property
00283 def execution_contexts(self):
00284 '''The execution contexts owned by this component.
00285
00286 May be an empty list if this component does not own any contexts.
00287 Members are of type @ref ExecutionContext.
00288
00289 '''
00290 return self._exec_contexts
00291
00292 @execution_contexts.setter
00293 def execution_contexts(self, execution_contexts):
00294 validate_attribute(execution_contexts, 'component.ExecutionContexts',
00295 expected_type=list, required=False)
00296 self._exec_contexts = execution_contexts
00297
00298 @property
00299 def participants(self):
00300 '''The list of participating components, if this component is a
00301 composite component.
00302
00303 Members are of type @ref Participant.
00304
00305 '''
00306 return self._participants
00307
00308 @participants.setter
00309 def participants(self, participants):
00310 validate_attribute(participants, 'component.Participants',
00311 expected_type=list, required=False)
00312 self._participants = participants
00313
00314 @property
00315 def comment(self):
00316 '''Comment about the component.
00317
00318 A brief comment about the component. May or may not be displayed in
00319 other tools. May be empty.
00320
00321 Part of the extended profile.
00322
00323 '''
00324 return self._comment
00325
00326 @comment.setter
00327 def comment(self, comment):
00328 validate_attribute(comment, 'component.ext.comment',
00329 expected_type=[str, unicode], required=False)
00330 self._comment = comment
00331
00332 @property
00333 def visible(self):
00334 '''Display the component in graphical tools.
00335
00336 This value controls whether graphical tools will display this component
00337 or not.
00338
00339 Part of the extended profile.
00340
00341 '''
00342 return self._visible
00343
00344 @visible.setter
00345 def visible(self, visible):
00346 validate_attribute(visible, 'component.ext.visible',
00347 expected_type=bool, required=False)
00348 self._visible = visible
00349
00350 @property
00351 def location(self):
00352 '''Specifies the position of the component in graphical tools.
00353
00354 Part of the extended profile.
00355
00356 '''
00357 return self._location
00358
00359 @location.setter
00360 def location(self, location):
00361 validate_attribute(location, 'component.ext.Location',
00362 expected_type=Location, required=True)
00363 self._location = location
00364
00365 @property
00366 def properties(self):
00367 '''Miscellaneous properties.
00368
00369 Stores key/value pair properties.
00370
00371 Part of the extended profile.
00372
00373 '''
00374 return self._properties
00375
00376 @properties.setter
00377 def properties(self, properties):
00378 validate_attribute(properties, 'component.ext.Properties',
00379 expected_type=dict, required=False)
00380 self._properties = properties
00381
00382
00383
00384
00385 def get_configuration_set_by_id(self, id):
00386 '''Finds a configuration set in the component by its ID.
00387
00388 @param id The ID of the configuration set to search for.
00389 @return The ConfigurationSet object for the set, or None if it was not
00390 found.
00391
00392 '''
00393 for cs in self.configuration_sets:
00394 if cs.id == id:
00395 return cs
00396 return None
00397
00398
00399
00400
00401 def parse_xml_node(self, node):
00402 '''Parse an xml.dom Node object representing a component into this
00403 object.
00404
00405 '''
00406 self._reset()
00407
00408 self.id = node.getAttributeNS(RTS_NS, 'id')
00409 self.path_uri = node.getAttributeNS(RTS_NS, 'pathUri')
00410 if node.hasAttributeNS(RTS_NS, 'activeConfigurationSet'):
00411 self.active_configuration_set = node.getAttributeNS(RTS_NS,
00412 'activeConfigurationSet')
00413 else:
00414 self.active_configuration_set = ''
00415 self.instance_name = node.getAttributeNS(RTS_NS, 'instanceName')
00416 self.compositeType = comp_type.from_string(node.getAttributeNS(RTS_NS,
00417 'compositeType'))
00418 required = node.getAttributeNS(RTS_NS, 'isRequired')
00419 if required == 'true' or required == '1':
00420 self.is_required = True
00421 else:
00422 self.is_required = False
00423 self.comment = node.getAttributeNS(RTS_EXT_NS, 'comment')
00424 if node.hasAttributeNS(RTS_EXT_NS, 'visible'):
00425 visible = node.getAttributeNS(RTS_EXT_NS, 'visible')
00426 if visible.lower() == 'true' or visible == '1':
00427 self.visible = True
00428 else:
00429 self.visible = False
00430
00431
00432 for c in node.getElementsByTagNameNS(RTS_NS, 'DataPorts'):
00433 self._data_ports.append(DataPort().parse_xml_node(c))
00434 for c in node.getElementsByTagNameNS(RTS_NS, 'ServicePorts'):
00435 self._service_ports.append(ServicePort().parse_xml_node(c))
00436 for c in node.getElementsByTagNameNS(RTS_NS, 'ConfigurationSets'):
00437 self._config_sets.append(ConfigurationSet().parse_xml_node(c))
00438 for c in node.getElementsByTagNameNS(RTS_NS, 'ExecutionContexts'):
00439 self._exec_contexts.append(ExecutionContext().parse_xml_node(c))
00440 for c in node.getElementsByTagNameNS(RTS_NS, 'Participants'):
00441 self._participants.append(Participant().parse_xml_node(c))
00442
00443 c = node.getElementsByTagNameNS(RTS_EXT_NS, 'Location')
00444 if c.length > 0:
00445 if c.length > 1:
00446 raise InvalidRtsProfileNodeError('Location')
00447 self._location = Location().parse_xml_node(c[0])
00448 for c in get_direct_child_elements_xml(node, prefix=RTS_EXT_NS,
00449 local_name='Properties'):
00450 name, value = parse_properties_xml(c)
00451 self._properties[name] = value
00452
00453 return self
00454
00455 def save_xml(self, doc, element):
00456 '''Save this component into an xml.dom.Element object.'''
00457 element.setAttributeNS(RTS_NS, RTS_NS_S + 'id', self.id)
00458 element.setAttributeNS(RTS_NS, RTS_NS_S + 'pathUri', self.path_uri)
00459 if self.active_configuration_set:
00460 element.setAttributeNS(RTS_NS, RTS_NS_S + 'activeConfigurationSet',
00461 self.active_configuration_set)
00462 element.setAttributeNS(RTS_NS, RTS_NS_S + 'instanceName',
00463 self.instance_name)
00464 element.setAttributeNS(RTS_NS, RTS_NS_S + 'compositeType',
00465 comp_type.to_string(self.composite_type))
00466 element.setAttributeNS(RTS_NS, RTS_NS_S + 'isRequired',
00467 str(self.is_required).lower())
00468 if self.comment:
00469 element.setAttributeNS(RTS_EXT_NS, RTS_EXT_NS_S + 'comment',
00470 self.comment)
00471 element.setAttributeNS(RTS_EXT_NS, RTS_EXT_NS_S + 'visible',
00472 str(self.visible).lower())
00473 for port in self.data_ports:
00474 new_element = doc.createElementNS(RTS_NS, RTS_NS_S + 'DataPorts')
00475 port.save_xml(doc, new_element)
00476 element.appendChild(new_element)
00477 for port in self.service_ports:
00478 new_element = doc.createElementNS(RTS_NS,
00479 RTS_NS_S + 'ServicePorts')
00480 port.save_xml(doc, new_element)
00481 element.appendChild(new_element)
00482 for cs in self.configuration_sets:
00483 new_element = doc.createElementNS(RTS_NS,
00484 RTS_NS_S + 'ConfigurationSets')
00485 cs.save_xml(doc, new_element)
00486 element.appendChild(new_element)
00487 for ec in self.execution_contexts:
00488 new_element = doc.createElementNS(RTS_NS,
00489 RTS_NS_S + 'ExecutionContexts')
00490 ec.save_xml(doc, new_element)
00491 element.appendChild(new_element)
00492 for p in self.participants:
00493 new_element = doc.createElementNS(RTS_NS,
00494 RTS_NS_S + 'Participants')
00495 p.save_xml(doc, new_element)
00496 element.appendChild(new_element)
00497 new_element = doc.createElementNS(RTS_EXT_NS,
00498 RTS_EXT_NS_S + 'Location')
00499 self._location.save_xml(doc, new_element)
00500 element.appendChild(new_element)
00501 for p in self.properties:
00502 new_prop_element = doc.createElementNS(RTS_EXT_NS,
00503 RTS_EXT_NS_S + 'Properties')
00504 properties_to_xml(new_prop_element, p, self.properties[p])
00505 element.appendChild(new_prop_element)
00506
00507
00508
00509
00510 def parse_yaml(self, y):
00511 '''Parse a YAML specification of a component into this object.'''
00512 self._reset()
00513 self.id = y['id']
00514 self.path_uri = y['pathUri']
00515 if 'activeConfigurationSet' in y:
00516 self.active_configuration_set = y['activeConfigurationSet']
00517 else:
00518 self.active_configuration_set = ''
00519 self.instance_name = y['instanceName']
00520 self.compositeType = comp_type.from_string(y['compositeType'])
00521 required = y['isRequired']
00522 if required == 'true' or required == '1':
00523 self.is_required = True
00524 else:
00525 self.is_required = False
00526 if RTS_EXT_NS_YAML + 'comment' in y:
00527 self.comment = y[RTS_EXT_NS_YAML + 'comment']
00528 self.visible = False
00529 if RTS_EXT_NS_YAML + 'visible' in y:
00530 visible = y.get(RTS_EXT_NS_YAML + 'visible')
00531 if visible == True or visible == 'true' or visible == 'True':
00532 self.visible = True
00533
00534
00535 if 'dataPorts' in y:
00536 for p in y.get('dataPorts'):
00537 self._data_ports.append(DataPort().parse_yaml(p))
00538 if 'servicePorts' in y:
00539 for p in y.get('servicePorts'):
00540 self._service_ports.append(ServicePort().parse_yaml(p))
00541 if 'configurationSets' in y:
00542 for p in y.get('configurationSets'):
00543 self._config_sets.append(ConfigurationSet().parse_yaml(p))
00544 if 'executionContexts' in y:
00545 for p in y.get('executionContexts'):
00546 self._exec_contexts.append(ExecutionContext().parse_yaml(p))
00547 if 'participants' in y:
00548 for p in y.get('participants'):
00549 self._participants.append(Participant().parse_yaml(p))
00550
00551
00552 if RTS_EXT_NS_YAML + 'location' in y:
00553 l = y[RTS_EXT_NS_YAML + 'location']
00554 self._location = Location().parse_yaml(l)
00555 if RTS_EXT_NS_YAML + 'properties' in y:
00556 for p in y.get(RTS_EXT_NS_YAML + 'properties'):
00557 if 'value' in p:
00558 value = p['value']
00559 else:
00560 value = None
00561 self._properties[p['name']] = value
00562
00563 return self
00564
00565 def to_dict(self):
00566 d = {'id': self.id,
00567 'pathUri': self.path_uri,
00568 'instanceName': self.instance_name,
00569 'compositeType': comp_type.to_string(self.composite_type),
00570 'isRequired': str(self.is_required).lower(),
00571 RTS_EXT_NS_YAML + 'visible': str(self.visible).lower()}
00572 if self.active_configuration_set:
00573 d['activeConfigurationSet'] = self.active_configuration_set
00574 if self.comment:
00575 d[RTS_EXT_NS_YAML + 'comment'] = self.comment
00576
00577 ports = []
00578 for p in self.data_ports:
00579 ports.append(p.to_dict())
00580 if ports:
00581 d['dataPorts'] = ports
00582 ports = []
00583 for p in self.service_ports:
00584 ports.append(p.to_dict())
00585 if ports:
00586 d['servicePorts'] = ports
00587 sets = []
00588 for cs in self.configuration_sets:
00589 sets.append(cs.to_dict())
00590 if sets:
00591 d['configurationSets'] = sets
00592 ecs = []
00593 for ec in self.execution_contexts:
00594 ecs.append(ec.to_dict())
00595 if ecs:
00596 d['executionContexts'] = ecs
00597 participants = []
00598 for p in self.participants:
00599 participants.append(p.to_dict())
00600 if participants:
00601 d['participants'] = participants
00602
00603 d[RTS_EXT_NS_YAML + 'location'] = self._location.to_dict()
00604 props = []
00605 for name in self.properties:
00606 p = {'name': name}
00607 if self.properties[name]:
00608 p['value'] = str(self.properties[name])
00609 props.append(p)
00610 if props:
00611 d[RTS_EXT_NS_YAML + 'properties'] = props
00612
00613 return d
00614
00615
00616
00617
00618 def _reset(self):
00619
00620
00621
00622 self._id = ''
00623 self._path_uri = ''
00624 self._active_config_set = ''
00625 self._instance_name = ''
00626 self._composite_type = comp_type.NONE
00627 self._is_required = False
00628
00629 self._data_ports = []
00630 self._service_ports = []
00631 self._config_sets = []
00632 self._exec_contexts = []
00633 self._participants = []
00634
00635 self._comment = ''
00636 self._visible = True
00637 self._location = Location()
00638 self._properties = {}
00639
00640
00641
00642