def __init__(self, graph): self.loader = ComponentLoader() self.processes = Processes(self, self.loader) self.connections = Edges(self) self.graph = graph self.startupDate = datetime.now()
def __init__ (self, graph): self.loader = ComponentLoader() self.processes = Processes(self, self.loader) self.connections = Edges(self) self.graph = graph self.startupDate = datetime.now()
class Network (EventEmitter): @classmethod def create (cls, graph, delayed = False): network = cls(graph) d = defer.Deferred() def networkReady (network): d.callback(network) # Send IIPs network.start() def componentsLoaded (components): # Empty network, no need to connect it up if len(graph.nodes) == 0: return networkReady(network) # In case of delayed execution we don't wire it up if delayed: return d.callback(network) # Wire the network up and start execution network.connect().addCallbacks(networkReady, d.errback) # Ensure components are loaded before continuing network.loader.listComponents().addCallbacks(componentsLoaded, d.errback) return d def __init__ (self, graph): self.loader = ComponentLoader() self.processes = Processes(self, self.loader) self.connections = Edges(self) self.graph = graph self.startupDate = datetime.now() @property def uptime (self): return (datetime.now() - self.startupDate).total_seconds() running = False connectionCount = 0 def increaseConnections (self): if self.connectionCount == 0 and not self.running: self.running = True # Otherwise can get multiple start events during IIP sending. self.emit("start", start = self.startupDate) self.connectionCount += 1 def decreaseConnections (self): self.connectionCount -= 1 if self.connectionCount == 0: self._end() @debounce(0.01) def _end (self): if self.connectionCount: return self.running = False self.emit("end", start = self.startupDate, end = datetime.now(), uptime = self.uptime ) def load (self, component, metadata = None): return self.loader.load(component, metadata) @defer.inlineCallbacks def connect (self): for node in self.graph.nodes: yield self.processes.add(**node) for edge in self.graph.edges: yield self.connections.add(**edge) for iip in self.graph.initials: yield self.connections.addInitial(**iip) self.subscribeGraph() defer.returnValue(self) def connectPort (self, socket, process, port, index, inbound): if inbound == True: socket.tgt = { "process": process, "port": port, "index": index } try: process.component.inPorts[port] except (AttributeError, KeyError): raise Error ("No inport '{:s}' defined in process {:s} ({:s})".format(port, process.id, socket.id)) if process.component.inPorts[port].addressable: return process.component.inPorts[port].attach(socket, index) return process.component.inPorts[port].attach(socket) else: socket.src = { "process": process, "port": port, "index": index } try: process.component.outPorts[port] except (AttributeError, KeyError): raise Error ("No outport '{:s}' defined in process {:s} ({:s})".format(port, process.id, socket.id)) if process.component.outPorts[port].addressable: return process.component.outPorts[port].attach(socket, index) return process.component.outPorts[port].attach(socket) # Subscribe to events from all connected sockets and re-emit them def subscribeSocket (self, socket): def socketevent (event, data): if event == "connect": self.increaseConnections() elif event == "disconnect": self.decreaseConnections() data["id"] = socket.id data["socket"] = socket self.emit(event, **data) for event in ('connect', 'begingroup', 'data', 'endgroup', 'disconnect'): socket.on(event, functools.partial(socketevent, event)) def subscribeGraph (self): # A NoFlo graph may change after network initialization. # For this, the network subscribes to the change events from # the graph. # # In graph we talk about nodes and edges. Nodes correspond # to NoFlo processes, and edges to connections between them. graphOps = deque() processing = False def registerOp (op, details): graphOps.append({ "op": op, "details": details }) if not processing: processOps() def error (reason): # TODO: log. print ("subscribeGraph: Error: " + str(reason)) processOps() def processOps (result = None): try: op = graphOps.popleft() processing = True op["op"](*op["details"]).addCallbacks(processOps, error) except IndexError: processing = False def handler (event, item, op, keys): def subscribeGraphHandler (data): d = data[item] a = [] for k in keys: try: a.append(d[k]) except: a.append(None) registerOp(op, a) return subscribeGraphHandler for event, item, op, keys in ( ("addNode", "node", self.processes.add, ("id", "component", "metadata")), ("removeNode", "node", self.processes.remove, ("id",)), ("addEdge", "edge", self.connections.add, ("src", "tgt", "metadata")), ("removeEdge", "edge", self.connections.remove, ("src", "tgt")), ("addInitial", "edge", self.connections.addInitial, ("src", "tgt", "metadata")), ("removeInitial", "edge", self.connections.removeInitial, ("tgt",)), ): self.graph.on(event, handler(event, item, op, keys)) @self.graph.on("renameNode") def subscribeGraphHandler (data): registerOp(self.processes.rename, { oldId: data["old"], newId: data["new"] }) def start (self): self.connections.sendInitials() def stop (self): # Disconnect all connections for connection in self.connections: if connection.connected: connection.disconnect() # Tell processes to shut down for process in self.processes: process.component.shutdown()
class Network(EventEmitter): @classmethod def create(cls, graph, delayed=False): network = cls(graph) d = defer.Deferred() def networkReady(network): d.callback(network) # Send IIPs network.start() def componentsLoaded(components): # Empty network, no need to connect it up if len(graph.nodes) == 0: return networkReady(network) # In case of delayed execution we don't wire it up if delayed: return d.callback(network) # Wire the network up and start execution network.connect().addCallbacks(networkReady, d.errback) # Ensure components are loaded before continuing network.loader.listComponents().addCallbacks(componentsLoaded, d.errback) return d def __init__(self, graph): self.loader = ComponentLoader() self.processes = Processes(self, self.loader) self.connections = Edges(self) self.graph = graph self.startupDate = datetime.now() @property def uptime(self): return (datetime.now() - self.startupDate).total_seconds() running = False connectionCount = 0 def increaseConnections(self): if self.connectionCount == 0 and not self.running: self.running = True # Otherwise can get multiple start events during IIP sending. self.emit("start", start=self.startupDate) self.connectionCount += 1 def decreaseConnections(self): self.connectionCount -= 1 if self.connectionCount == 0: self._end() @debounce(0.01) def _end(self): if self.connectionCount: return self.running = False self.emit("end", start=self.startupDate, end=datetime.now(), uptime=self.uptime) def load(self, component, metadata=None): return self.loader.load(component, metadata) @defer.inlineCallbacks def connect(self): for node in self.graph.nodes: yield self.processes.add(**node) for edge in self.graph.edges: yield self.connections.add(**edge) for iip in self.graph.initials: yield self.connections.addInitial(**iip) self.subscribeGraph() defer.returnValue(self) def connectPort(self, socket, process, port, index, inbound): if inbound == True: socket.tgt = {"process": process, "port": port, "index": index} try: process.component.inPorts[port] except (AttributeError, KeyError): raise Error( "No inport '{:s}' defined in process {:s} ({:s})".format( port, process.id, socket.id)) if process.component.inPorts[port].addressable: return process.component.inPorts[port].attach(socket, index) return process.component.inPorts[port].attach(socket) else: socket.src = {"process": process, "port": port, "index": index} try: process.component.outPorts[port] except (AttributeError, KeyError): raise Error( "No outport '{:s}' defined in process {:s} ({:s})".format( port, process.id, socket.id)) if process.component.outPorts[port].addressable: return process.component.outPorts[port].attach(socket, index) return process.component.outPorts[port].attach(socket) # Subscribe to events from all connected sockets and re-emit them def subscribeSocket(self, socket): def socketevent(event, data): if event == "connect": self.increaseConnections() elif event == "disconnect": self.decreaseConnections() data["id"] = socket.id data["socket"] = socket self.emit(event, **data) for event in ('connect', 'begingroup', 'data', 'endgroup', 'disconnect'): socket.on(event, functools.partial(socketevent, event)) def subscribeGraph(self): # A NoFlo graph may change after network initialization. # For this, the network subscribes to the change events from # the graph. # # In graph we talk about nodes and edges. Nodes correspond # to NoFlo processes, and edges to connections between them. graphOps = deque() processing = False def registerOp(op, details): graphOps.append({"op": op, "details": details}) if not processing: processOps() def error(reason): # TODO: log. print("subscribeGraph: Error: " + str(reason)) processOps() def processOps(result=None): try: op = graphOps.popleft() processing = True op["op"](*op["details"]).addCallbacks(processOps, error) except IndexError: processing = False def handler(event, item, op, keys): def subscribeGraphHandler(data): d = data[item] a = [] for k in keys: try: a.append(d[k]) except: a.append(None) registerOp(op, a) return subscribeGraphHandler for event, item, op, keys in ( ("addNode", "node", self.processes.add, ("id", "component", "metadata")), ("removeNode", "node", self.processes.remove, ("id", )), ("addEdge", "edge", self.connections.add, ("src", "tgt", "metadata")), ("removeEdge", "edge", self.connections.remove, ("src", "tgt")), ("addInitial", "edge", self.connections.addInitial, ("src", "tgt", "metadata")), ("removeInitial", "edge", self.connections.removeInitial, ("tgt", )), ): self.graph.on(event, handler(event, item, op, keys)) @self.graph.on("renameNode") def subscribeGraphHandler(data): registerOp(self.processes.rename, { oldId: data["old"], newId: data["new"] }) def start(self): self.connections.sendInitials() def stop(self): # Disconnect all connections for connection in self.connections: if connection.connected: connection.disconnect() # Tell processes to shut down for process in self.processes: process.component.shutdown()