from vice.data import expr from twisted.python import log import exceptions, sys, os, re, StringIO from Ft import Lib from Ft.Xml import XPath, Domlette, EMPTY_NAMESPACE from vice import VICE_XRC_PATH, VICE_BITMAP_PATH, VICE_PALETTE_PATH XSDNS = 'http://www.w3.org/2001/XMLSchema' NSS = {'xsd': XSDNS} ending_numeric = re.compile("^([^0-9]+)([0-9]+)$") class Id: def __init__(self, _id=None): if _id: self._id = _id else: self._id = Lib.Uuid.UuidAsString(Lib.Uuid.GenerateUuid()) def GetId(self): return self._id class CallbackMessageType: pass class NETWORK_CREATE_BEGIN(CallbackMessageType): pass class NETWORK_CREATE_END(CallbackMessageType): pass class NETWORK_SET_SIZE(CallbackMessageType): pass class NETWORK_ADD_NODE(CallbackMessageType): pass class NETWORK_DELETE_NODE(CallbackMessageType): pass class NETWORK_ADD_RELATION(CallbackMessageType): pass class NETWORK_DELETE_RELATION(CallbackMessageType): pass class PORT_CREATE_BEGIN(CallbackMessageType): pass class PORT_CREATE_END(CallbackMessageType): pass class NODE_CREATE_BEGIN(CallbackMessageType): pass class NODE_CREATE_END(CallbackMessageType): pass class NODE_ADD_IN_PORT(CallbackMessageType): pass class NODE_ADD_OUT_PORT(CallbackMessageType): pass class NODE_SET_PARAMETER_VALUE(CallbackMessageType): pass class NODE_SET_PARAMETER_DOM(CallbackMessageType): pass class NODE_SET_POSITION(CallbackMessageType): pass class NODE_ADD_SOURCE_LINK(CallbackMessageType): pass class NODE_ADD_DEST_LINK(CallbackMessageType): pass class LINK_CREATE_BEGIN(CallbackMessageType): pass class LINK_CREATE_END(CallbackMessageType): pass class RELATION_CREATE_BEGIN(CallbackMessageType): pass class RELATION_CREATE_END(CallbackMessageType): pass class CallbackMessageException(exceptions.Exception): pass class NODE_CREATE_CALLBACK_FAILED(CallbackMessageException): pass class MonitorCallback: def __init__(self): pass def callback(self, *args, **kwargs): log.debug("Callback: message=%s, object classes=%s" % (args[0], map(lambda x: x.__class__, args[1:]))) m = MonitorCallback() #CB_DEFAULT=m.callback CB_DEFAULT=None ## TODO: someday, serialization and deserialization will be ## simplified: the code will just walk the moml dom ## recursively, instantiating classes as they are defined in ## the moml file, based on vice.actor.whatever, ## vice.data.expr.whatever, rather than building native Node ## objects. class Network: class NodeAlreadyInNetwork(exceptions.Exception): pass def __init__(self, path=None, cb=CB_DEFAULT): self._cb = cb self._nodes = {} self._relations = [] self._size_x = 1024 self._size_y = 768 if self._cb: self._cb(*[NETWORK_CREATE_BEGIN, self]) if path: self._path = path self.deserialize(path) else: self._path = None if self._cb: self._cb(*[NETWORK_CREATE_END, self]) def deserialize(self, path): file_uri = Lib.Uri.OsPathToUri(path, attemptAbsolute=1) doc = Domlette.NonvalidatingReader.parseUri(file_uri) ## TODO: replace w/ local reference, so this doesn't require an outgoing network connection ctx = XPath.Context.Context(doc, processorNss=NSS) ## collect network attributes network_size_node = XPath.Evaluate(u'/entity/property[@class="vice.actor.gui.SizeAttribute"]', ctx) if network_size_node: network_size_value = network_size_node[0].getAttributeNS(EMPTY_NAMESPACE, "value") x, y = map(int, network_size_value[1:-1].split(",")) else: x, y = 1024, 768 self.SetSize(x, y) ## retrieve all entities and convert to nodes entities = XPath.Evaluate(u'/entity/entity', ctx) ## instantiate nodes for entity in entities: location_node = entity.xpath('property[@name="_location"]', EMPTY_NAMESPACE)[0] location_value = location_node.getAttributeNS(EMPTY_NAMESPACE, "value") x, y = map(int, location_value[1:-1].split(",")) actor_class = entity.getAttributeNS(EMPTY_NAMESPACE, "class") actor_id= entity.getAttributeNS(EMPTY_NAMESPACE, "id") node = self.AddNode(entity, actor_id, actor_class, (x,y)) ## No longer need to do this (?) since the node constructor calls AddParameters ## ## TODO: switch this so that multiple types of parameters are supported ## ## maybe try to iterate over all property[@class=, then try instantiating each klass by name ## parameters = entity.xpath('property', EMPTY_NAMESPACE) ## for parameter in parameters: ## self.AddParameter(node, parameter) ## retrieve all edges(links/relations) relations = XPath.Evaluate(u'/entity/relation', ctx) for relation in relations: name = relation.getAttributeNS(EMPTY_NAMESPACE, "name") ## support ViCE-style MoML if name.startswith("relation."): relation_name, relation_id = name.split(".") ## support (retch) Ptolemy-style MoML else: relation_name, relation_id = ending_numeric.match(name).groups() source, dest = XPath.Evaluate(u'/entity/link[@relation="%s"]' % name, ctx) source_port = source.getAttributeNS(EMPTY_NAMESPACE, "port") dest_port = dest.getAttributeNS(EMPTY_NAMESPACE, "port") source_nodename = str(source_port.split(".")[0]) dest_nodename = str(dest_port.split(".")[0]) source_id = str(source_port.split(".")[1]) dest_id = str(dest_port.split(".")[1]) source_port_name = str(source_port.split(".")[2]) dest_port_name = str(dest_port.split(".")[2]) source_nodename_id = ".".join([source_nodename, source_id]) dest_nodename_id = ".".join([dest_nodename, dest_id]) source_node = self.GetNode(source_id) dest_node = self.GetNode(dest_id) ## Consider adding a 'SetRelation' method on Links, so we can access all relations for a node's links source_link = Link(source_node, source_node.GetOutPort(source_port_name), cb=self._cb) dest_link = Link(dest_node, dest_node.GetInPort(dest_port_name), cb=self._cb) r = Relation(relation_id, source_link, dest_link, cb=self._cb) self.AddRelation(r) source_node.AddSourceLink(source_link) dest_node.AddDestLink(dest_link) def serialize(self): NETWORK_DOC = """ """ doc = Domlette.NonvalidatingReader.parseString(NETWORK_DOC) ctx = XPath.Context.Context(doc) root = XPath.Evaluate(u'//entity', context=ctx)[0] ## Add the director property (not actually used by vice yet) director = doc.createElementNS(EMPTY_NAMESPACE, 'property') director.setAttributeNS(EMPTY_NAMESPACE, 'name', 'PN Director') director.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.domains.pn.kernel.PNDirector') director_location = doc.createElementNS(EMPTY_NAMESPACE, 'property') director_location.setAttributeNS(EMPTY_NAMESPACE, 'name', '_location') director_location.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.kernel.util.Location') director_location.setAttributeNS(EMPTY_NAMESPACE, 'value', '{0,0}') director.appendChild(director_location) root.appendChild(director) windowProperties = doc.createElementNS(EMPTY_NAMESPACE, 'property') windowProperties.setAttributeNS(EMPTY_NAMESPACE, 'name', '_windowProperties') windowProperties.setAttributeNS(EMPTY_NAMESPACE, 'value', '{bounds={568, 195, 1038, 794}, maximized=false}') windowProperties.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.actor.gui.WindowPropertiesAttribute') root.appendChild(windowProperties) vergilSize = doc.createElementNS(EMPTY_NAMESPACE, 'property') vergilSize.setAttributeNS(EMPTY_NAMESPACE, 'name', '_vergilSize') vergilSize.setAttributeNS(EMPTY_NAMESPACE, 'value', '[%d,%d]' % self.GetSize()) vergilSize.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.actor.gui.SizeAttribute') root.appendChild(vergilSize) vergilZoomFactor = doc.createElementNS(EMPTY_NAMESPACE, 'property') vergilZoomFactor.setAttributeNS(EMPTY_NAMESPACE, 'name', '_vergilZoomFactor') vergilZoomFactor.setAttributeNS(EMPTY_NAMESPACE, 'value', '"1.0"') vergilZoomFactor.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.data.expr.ExpertParameter') root.appendChild(vergilZoomFactor) vergilCenter = doc.createElementNS(EMPTY_NAMESPACE, 'property') vergilCenter.setAttributeNS(EMPTY_NAMESPACE, 'name', '_vergilCenter') vergilCenter.setAttributeNS(EMPTY_NAMESPACE, 'value', '{411.5, 338.0}') vergilCenter.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.data.expr.ExpertParameter') root.appendChild(vergilCenter) ## We maintain a random number index to uniquify nodes (unlike ## ptolemy, which uses an ambiguous numbering scheme) for node_id in self._nodes.keys(): node = self._nodes[node_id] entity = doc.createElementNS(EMPTY_NAMESPACE, 'entity') entity.setAttributeNS(EMPTY_NAMESPACE, 'name', node.GetName()) entity.setAttributeNS(EMPTY_NAMESPACE, 'class', node.__module__) entity.setAttributeNS(EMPTY_NAMESPACE, 'id', node.GetId()) ## Add a location tag location = doc.createElementNS(EMPTY_NAMESPACE, 'property') location.setAttributeNS(EMPTY_NAMESPACE, 'name', '_location') location.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.kernel.util.Location') x, y = node.GetPosition() location.setAttributeNS(EMPTY_NAMESPACE, 'value', '{%d, %d}' % (x,y)) entity.appendChild(location) ## Add an icon representation icon = doc.createElementNS(EMPTY_NAMESPACE, 'property') icon.setAttributeNS(EMPTY_NAMESPACE, 'name', '_icon') icon.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.vergil.icon.BoxedValueIcon') icon.setAttributeNS(EMPTY_NAMESPACE, 'value', 'value') entity.appendChild(icon) ## Visit all the parameters in the node (ptolemy only ## outputs params which have non-default values) ## TODO: support other types of parameters for parameter_name in node.GetParameters(): value = node.GetParameterValue(parameter_name) param_type = node.GetParameterType(parameter_name) parameter_dom = node.GetParameterDom(parameter_name) property = doc.createElementNS(EMPTY_NAMESPACE, 'property') ## This doesn't belong here! if param_type == 'vice.data.expr.ChoiceParameter': property.setAttributeNS(EMPTY_NAMESPACE, 'values', parameter_dom.getAttributeNS(EMPTY_NAMESPACE, "values")) property.setAttributeNS(EMPTY_NAMESPACE, 'name', parameter_name) ## Warning Will Robinson! Crappy str() ahead! property.setAttributeNS(EMPTY_NAMESPACE, 'value', str(value)) ## should do a lookup into the individual control here... ## (for example, to print values) property.setAttributeNS(EMPTY_NAMESPACE, 'class', param_type) entity.appendChild(property) attrName = doc.createElementNS(EMPTY_NAMESPACE, 'property') attrName.setAttributeNS(EMPTY_NAMESPACE, 'name', 'attributeName') attrName.setAttributeNS(EMPTY_NAMESPACE, 'value', parameter_name) attrName.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.kernel.util.StringAttribute') icon.appendChild(attrName) ## Visit all the inports and outports in the node (ptolemy only outputs ports which have been added after creation, but we list them all) for port_pair in (('input', node.GetInPorts, node.GetInPort), ('output', node.GetOutPorts, node.GetOutPort)): port_type = port_pair[0] port_func = port_pair[1] port_get_func = port_pair[2] for port_name in port_func(): port = port_get_func(port_name) port_elem = doc.createElementNS(EMPTY_NAMESPACE, 'port') gn = port.GetName() port_elem.setAttributeNS(EMPTY_NAMESPACE, 'name', port.GetName()) port_elem.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.actor.TypedIOPort') property = doc.createElementNS(EMPTY_NAMESPACE, 'property') property.setAttributeNS(EMPTY_NAMESPACE, 'name', port_type) port_elem.appendChild(property) property = doc.createElementNS(EMPTY_NAMESPACE, 'property') property.setAttributeNS(EMPTY_NAMESPACE, 'name', '_type') property.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.actor.TypeAttribute') property.setAttributeNS(EMPTY_NAMESPACE, 'value', 'unknown') port_elem.appendChild(property) entity.appendChild(port_elem) root.appendChild(entity) for relation in self._relations: relation_element = doc.createElementNS(EMPTY_NAMESPACE, 'relation') ## Only write ViCE-style MoML ('relation' and the numeric ID are seperated by a dot) relation_element.setAttributeNS(EMPTY_NAMESPACE, 'name', 'relation.%s' % relation.GetId()) relation_element.setAttributeNS(EMPTY_NAMESPACE, 'class', 'vice.actor.TypedIORelation') root.appendChild(relation_element) for relation in self._relations: link_source_element = doc.createElementNS(EMPTY_NAMESPACE, 'link') link_source = relation.GetSourceLink() node_source = link_source.GetNode() link_source_element.setAttributeNS(EMPTY_NAMESPACE, 'port', "%s.%s.%s" % (node_source.GetName(), node_source.GetId(), link_source.GetPort().GetName())) ## Only write ViCE-style MoML link_source_element.setAttributeNS(EMPTY_NAMESPACE, 'relation', 'relation.%s' % relation.GetId()) link_dest_element = doc.createElementNS(EMPTY_NAMESPACE, 'link') link_dest = relation.GetDestLink() node_dest = link_dest.GetNode() link_dest_element.setAttributeNS(EMPTY_NAMESPACE, 'port', "%s.%s.%s" % (node_dest.GetName(), node_dest.GetId(), link_dest.GetPort().GetName())) ## Only write ViCE-style MoML link_dest_element.setAttributeNS(EMPTY_NAMESPACE, 'relation', 'relation.%s' % relation.GetId()) root.appendChild(link_source_element) root.appendChild(link_dest_element) buf = StringIO.StringIO() Domlette.PrettyPrint(doc, buf) xmlString = buf.getvalue() buf.close() return xmlString def GetSize(self): return self._size_x, self._size_y def SetSize(self, x, y): self._size_x = x self._size_y = y if self._cb: self._cb(*[NETWORK_SET_SIZE, self, x, y]) ## actor_class, actor_id, and xy are now redundant with actor_dom being passed in! def AddNode(self, actor_dom, actor_id, actor_class, xy): log.debug("AddNode: %s, %s" % (actor_id, actor_class)) actor_classname = actor_class.split(".")[-1] ## make an instance of the defined class ## If the module was already loaded, do a reload (makes debugging easier) if actor_class in sys.modules: module = sys.modules[actor_class] reload(module) else: __import__(actor_class, globals(), locals(), []) module = sys.modules[actor_class] klass = module.__dict__[actor_classname] try: node = klass(actor_dom, actor_id, xy, self._cb) except NODE_CREATE_CALLBACK_FAILED: ## we don't really handle this here; allow somebody using the model framework to use the exception intelligently raise ## Can't see how this could ever happen! if node.GetId() in self._nodes: raise Network.NodeAlreadyInNetwork, "Node %s already in network" self._nodes[node.GetId()] = node if self._cb: self._cb(*[NETWORK_ADD_NODE, self, node]) return node def DeleteNode(self, node): del self._nodes[node.GetId()] ## this is wrong; each node should have reference to the ## relations its part of, or the link should have a pointer to ## its relation node_source_relations = filter(lambda x: x.GetSourceLink().GetNode() == node, self._relations) node_dest_relations = filter(lambda x: x.GetDestLink().GetNode() == node, self._relations) ## ## Remove all relations map(self.DeleteRelation, node_source_relations) map(self.DeleteRelation, node_dest_relations) if self._cb: self._cb(*[NETWORK_DELETE_NODE, self, node]) def GetNode(self, node_name): return self._nodes[node_name] def GetNodes(self): return self._nodes ## def AddParameter(self, node, parameter): ## node.AddParameter(parameter) def AddRelation(self, relation): self._relations.append(relation) relation.GetSourceLink().SetRelation(relation) relation.GetDestLink().SetRelation(relation) if self._cb: self._cb(*[NETWORK_ADD_RELATION, self, relation]) def DeleteRelation(self, relation): if self._cb: self._cb(*[NETWORK_DELETE_RELATION, self, relation]) self._relations.remove(relation) ## Delete the links that compose this relation source_link = relation.GetSourceLink() source_node = source_link.GetNode() dest_link = relation.GetDestLink() dest_node = dest_link.GetNode() source_node.DeleteSourceLink(source_link) dest_node.DeleteDestLink(dest_link) def GetRelations(self): return self._relations def __str__(self): result= "Network: path=%s" % self._path node_result = "\t" + "\n\t".join(map(lambda x: str(self._nodes[x]), self._nodes.keys())) relation_result = "\t" + "\n\t".join(map(lambda x: str(x), self._relations)) return "\n".join([result, node_result, relation_result]) def __del__(self): return ## try: ## for node in self._nodes.keys(): ## ## self.DeleteNode(node) ## no = self.GetNode(node) ## self.DeleteNode(no) ## except AttributeError: ## log.err("Error in Network destructor: attribute error looping on nodes") ## except KeyError: ## log.err("Error in Network destructor: cannot find node %s" % node) ## try: ## for relation in self._relations: ## self.DeleteRelation(relation) ## except AttributeError: ## log.err("Error in Network destructor: cannot attribute error looping on relations") class Port: def __init__(self, name, node, cb=CB_DEFAULT): log.debug("Creating port with name=%s" % name) self._name = name self._node = node self._cb = cb if self._cb: self._cb(*[PORT_CREATE_BEGIN, self]) if self._cb: self._cb(*[PORT_CREATE_END, self]) def GetName(self): return self._name def GetNode(self): return self._node def __str__(self): return "Port: name=%s" % self._name def __del__(self): pass class Node(Id): class NodeCreationFailed(exceptions.Exception): pass class NonexistentParameterValue(exceptions.Exception): pass class MissingInputOrOutputProperty(exceptions.Exception): def __init__(self, portName): self._portName = portName class LinkAlreadyInNode(exceptions.Exception): def __init__(self, link): self._link = link class SourceLinkAlreadyInNode(LinkAlreadyInNode): pass class DestLinkAlreadyInNode(LinkAlreadyInNode): pass class PortNameAlreadyExists(exceptions.Exception): def __init__(self, portName): self._portName = portName class ParameterNameAlreadyExists(exceptions.Exception): def __init__(self, parameterName): self._parameterName = parameterName def __init__(self, dom=None, _id=None, xy=None, cb=CB_DEFAULT): self._cb = cb Id.__init__(self, _id) ## if no custom dom is provided, then use the default XML (in the actor's class definition) if not dom: self._dom = Domlette.NonvalidatingReader.parseString(self.xml).xpath("/entity")[0] ## Use a custom dom (from a saved network) to render this node else: self._dom = dom name = self._dom.getAttributeNS(EMPTY_NAMESPACE, "name") self.SetName(name) self._x = xy[0] self._y = xy[1] self._parameters = {} self._parameter_doms = {} self._parameter_order = [] self._source_links = [] self._dest_links = [] self._in_ports = {} self._out_ports = {} if self._cb: self._cb(*[NODE_CREATE_BEGIN, self]) log.debug("Node: calling AddPorts") self.AddPorts() self.AddParameters() if self._cb: self._cb(*[NODE_CREATE_END, self]) def AddParameters(self): ## Pull out the DOM representing the parameters parameters = self._dom.xpath('property', EMPTY_NAMESPACE) for parameter in parameters: if parameter.getAttributeNS(EMPTY_NAMESPACE, "class"): self.AddParameter(parameter) def AddParameter(self, parameter_dom, parameter_name=None, parameter_value=None): p_type = parameter_dom.getAttributeNS(EMPTY_NAMESPACE, "class") ## If this dom node is of a type that's a registered param/control, ## add the param/control to the node control_name = None control_value = None if parameter_name or parameter_value: ## create a copy of the dom first, so it doesn't change the class definition of the dom! ## is there an easier way to deepcopy a dom? buf = StringIO.StringIO() Domlette.PrettyPrint(parameter_dom, buf) xmlString = buf.getvalue() buf.close() parameter_dom = Domlette.NonvalidatingReader.parseString(xmlString).firstChild if parameter_name: parameter_dom.setAttributeNS(EMPTY_NAMESPACE, "name", parameter_name) if parameter_value: parameter_dom.setAttributeNS(EMPTY_NAMESPACE, "value", parameter_value) if not (parameter_name and parameter_value): try: control_class = expr.find(p_type) except KeyError: log.debug("Unknown p_type: %s" % p_type) else: if not control_name: control_name = parameter_dom.getAttributeNS(EMPTY_NAMESPACE, "name") if not control_value: control_value = parameter_dom.getAttributeNS(EMPTY_NAMESPACE, "value") if control_name in self._parameters: raise Node.ParameterNameAlreadyExists, control_name self._parameter_order.append(control_name) ## Set parameter control to have initial null values, so ## that the widget-to-parameter system has something to ## reference self.SetParameterDom(control_name, parameter_dom) self.SetParameterValue(control_name, control_value) ## This adds ports from the XML description of the actor, not the ports defined in the saved network. def AddPorts(self): ports = self._dom.xpath(u'port') log.debug("Node: AddPorts: %s" % ports) for port in ports: ## Pull out the DOM representing the node portName = port.getAttributeNS(EMPTY_NAMESPACE, "name") is_input = port.xpath(u'property[@name="input"]', EMPTY_NAMESPACE) is_output = port.xpath(u'property[@name="output"]', EMPTY_NAMESPACE) if is_input: self.AddInPort(portName) elif is_output: self.AddOutPort(portName) else: raise MissingInputOrOutputProperty, portName def AddInPort(self, portName): log.debug("AddInPort: %s, %s" % (self, portName)) if unicode(portName) in self.GetInPorts(): raise Node.PortNameAlreadyExists, portName port = Port(portName, self, cb=self._cb) self._in_ports[port.GetName()] = port if self._cb: self._cb(*[NODE_ADD_IN_PORT, port]) return port def AddOutPort(self, portName): log.debug("AddOutPort, self='%s', portName='%s'" % (self, portName)) if unicode(portName) in self.GetOutPorts(): raise Node.PortNameAlreadyExists, portName port = Port(portName, self, cb=self._cb) self._out_ports[port.GetName()] = port if self._cb: self._cb(*[NODE_ADD_OUT_PORT, port]) return port def GetInPorts(self): return self._in_ports.keys() def GetInPort(self, name): return self._in_ports[name] def GetOutPorts(self): return self._out_ports.keys() def GetOutPort(self, name): return self._out_ports[name] def SetPosition(self, xy): if self._cb: self._cb(*[NODE_SET_POSITION, self, xy]) self._x = xy[0] self._y = xy[1] def GetPosition(self): return self._x, self._y def SetName(self, name): self._name = name def GetName(self): return self._name def GetPosition(self): return self._x, self._y def GetParameterValue(self, name): if name in self._parameters: return self._parameters[name] else: raise Node.NonexistentParameterValue, name def GetParameterType(self, name): return self._parameter_doms[name].getAttributeNS(EMPTY_NAMESPACE, "class") def GetParameters(self): return self._parameter_order ## TODO: figure out how to support mutiple control widget types here def SetParameterValue(self, name, value): self._parameters[name] = value if self._cb: self._cb(*[NODE_SET_PARAMETER_VALUE, self, name, value]) def SetParameterDom(self, name, dom): self._parameter_doms[name] = dom if self._cb: self._cb(*[NODE_SET_PARAMETER_DOM, self, name, dom]) def GetParameterDom(self, name): return self._parameter_doms[name] def AddSourceLink(self, link): if link in self._source_links: raise SourceLinkAlreadyInNode, link self._source_links.append(link) def DeleteSourceLink(self, link): self._source_links.remove(link) def AddDestLink(self, link): if link in self._dest_links: raise DestLinkAlreadyInNode, link self._dest_links.append(link) def DeleteDestLink(self, link): self._dest_links.remove(link) def GetSourceLinks(self): return self._source_links def GetDestLinks(self): return self._dest_links def __str__(self): return "Node: id=%s, name=%s, in_ports=%s, out_ports=%s" % (self.GetId(), self._name, #self._source_links, #self._dest_links, map(str, self._in_ports), map(str, self._out_ports)) def __del__(self): pass class Relation(Id): def __init__(self, _id, source_link, dest_link, cb=CB_DEFAULT): Id.__init__(self, _id) self._source_link = source_link self._dest_link = dest_link self._cb = cb if self._cb: self._cb(*[RELATION_CREATE_BEGIN, self]) if self._cb: self._cb(*[RELATION_CREATE_END, self]) def GetSourceLink(self): return self._source_link def GetDestLink(self): return self._dest_link def __str__(self): return "Relation: id=%s, source %s dest %s" % (self.GetId(), self._source_link, self._dest_link) def __del__(self): pass class Link: class DestinationPortOccupied(exceptions.Exception): pass ## TODO: 'port' arg should just be a string rather than a port object, to ensure the port belongs to the node def __init__(self, node, port, cb=CB_DEFAULT): node_dest_links = node.GetDestLinks() if [dest_link for dest_link in node_dest_links if dest_link.GetPort() == port]: raise Link.DestinationPortOccupied self._node = node self._port = port self._relation = None self._cb = cb if self._cb: self._cb(*[LINK_CREATE_BEGIN, self]) if self._cb: self._cb(*[LINK_CREATE_END, self]) def GetNode(self): return self._node def GetPort(self): return self._port def SetRelation(self, relation): self._relation = relation def GetRelation(self): return self._relation ## Don't invoke _node's str; otherwise, we'd get str loops def __str__(self): return "Link, nodename=%s, nodeid=%s, portname=%s" % (self._node.GetName(), self._node.GetId(), self._port.GetName()) def __del__(self): pass from vice.autolog import autolog_module autolog_module()