/// <summary> /// Get a dynFunction from a guid, also stores type internally info for future instantiation. /// And add the compiled node to the enviro /// As a side effect, any of its dependent nodes are also initialized. /// </summary> /// <param name="environment">The environment from which to get the </param> /// <param name="guid">Open a definition from a path, without instantiating the nodes or dependents</param> public bool GetNodeInstance(Guid guid, out Function result) { var controller = dynSettings.Controller; if (!this.Contains(guid)) { result = null; return(false); } FunctionDefinition def = null; if (!this.IsInitialized(guid)) { if (!GetDefinitionFromPath(guid, out def)) { result = null; return(false); } } else { def = this.LoadedCustomNodes[guid]; } WorkspaceModel ws = def.WorkspaceModel; IEnumerable <string> inputs = ws.Nodes.Where(e => e is Symbol) .Select(s => (s as Symbol).InputSymbol); IEnumerable <string> outputs = ws.Nodes.Where(e => e is Output) .Select(o => (o as Output).Symbol); if (!outputs.Any()) { var topMost = new List <Tuple <int, NodeModel> >(); IEnumerable <NodeModel> topMostNodes = ws.GetTopMostNodes(); foreach (NodeModel topNode in topMostNodes) { foreach (int output in Enumerable.Range(0, topNode.OutPortData.Count)) { if (!topNode.HasOutput(output)) { topMost.Add(Tuple.Create(output, topNode)); } } } outputs = topMost.Select(x => x.Item2.OutPortData[x.Item1].NickName); } result = controller.DynamoViewModel.CreateFunction(inputs, outputs, def); result.NickName = ws.Name; return(true); }
public FScheme.Expression Compile() { IEnumerable <string> inputNames = null; IEnumerable <string> outputNames = null; // Get the internal nodes for the function WorkspaceModel functionWorkspace = this.WorkspaceModel; #region Find outputs // Find output elements for the node List <Output> outputs = functionWorkspace.Nodes.OfType <Output>().ToList(); var topMost = new List <Tuple <int, NodeModel> >(); // if we found output nodes, add select their inputs // these will serve as the function output if (outputs.Any()) { topMost.AddRange( outputs.Where(x => x.HasInput(0)).Select(x => x.Inputs[0])); outputNames = outputs.Select(x => x.Symbol); } else { // if there are no explicitly defined output nodes // get the top most nodes and set THEM as the output IEnumerable <NodeModel> topMostNodes = functionWorkspace.GetTopMostNodes(); NodeModel infinite = null; var outNames = new List <string>(); foreach (NodeModel topNode in topMostNodes) { if (topNode is Function && (topNode as Function).Definition == this) { infinite = topNode; continue; } foreach (int output in Enumerable.Range(0, topNode.OutPortData.Count)) { if (!topNode.HasOutput(output)) { topMost.Add(Tuple.Create(output, topNode)); outNames.Add(topNode.OutPortData[output].NickName); } } } if (infinite != null && outNames.Count == 0) { topMost.Add(Tuple.Create(0, infinite)); outNames.Add("∞"); } outputNames = outNames; } #endregion // color the node to define its connectivity foreach (var ele in topMost) { ele.Item2.ValidateConnections(); } //Find function entry point, and then compile var variables = functionWorkspace.Nodes.OfType <Symbol>().ToList(); inputNames = variables.Select(x => x.InputSymbol); //Update existing function nodes which point to this function to match its changes dynSettings.Controller.DynamoModel.AllNodes .OfType <Function>() .Where(el => el.Definition != null && el.Definition.FunctionId == this.FunctionId) .ToList() .ForEach(node => { node.SetInputs(inputNames); node.SetOutputs(outputNames); node.RegisterAllPorts(); }); //Call OnSave for all saved elements functionWorkspace.Nodes.ToList().ForEach(x => x.onSave()); INode top; var buildDict = new Dictionary <NodeModel, Dictionary <int, INode> >(); if (topMost.Count > 1) { InputNode node = new ExternalFunctionNode(FScheme.Value.NewList); int i = 0; foreach (var topNode in topMost) { string inputName = i.ToString(CultureInfo.InvariantCulture); node.AddInput(inputName); node.ConnectInput(inputName, new BeginNode()); try { var exp = topNode.Item2.Build(buildDict, topNode.Item1); node.ConnectInput(inputName, exp); } catch { } i++; } top = node; } else if (topMost.Count == 1) { top = topMost[0].Item2.Build(buildDict, topMost[0].Item1); } else { // if the custom node is empty, it will initially be an empty begin top = new BeginNode(); } // if the node has any outputs, we create a BeginNode in order to evaluate all of them // sequentially (begin evaluates a list of expressions) if (outputs.Any()) { var beginNode = new BeginNode(); List <NodeModel> hangingNodes = functionWorkspace.GetHangingNodes().ToList(); foreach (var tNode in hangingNodes.Select((x, index) => new { Index = index, Node = x })) { beginNode.AddInput(tNode.Index.ToString(CultureInfo.InvariantCulture)); beginNode.ConnectInput( tNode.Index.ToString(CultureInfo.InvariantCulture), tNode.Node.Build(buildDict, 0)); } beginNode.AddInput(hangingNodes.Count.ToString(CultureInfo.InvariantCulture)); beginNode.ConnectInput(hangingNodes.Count.ToString(CultureInfo.InvariantCulture), top); top = beginNode; } // make the anonymous function FScheme.Expression expression = Utils.MakeAnon( variables.Select(x => x.GUID.ToString()), top.Compile()); return(expression); }
/// <summary> /// Compiles this custom node definition, updating all UI instances to match /// inputs and outputs and registering new definition with the EngineController. /// </summary> public void Compile(DynamoModel dynamoModel, EngineController controller) { // If we are loading dyf file, dont compile it until all nodes are loaded // otherwise some intermediate function defintions will be created. // TODO: This is a hack, in reality we should be preventing this from being called at the Workspace.RequestSync() level --SJE if (IsBeingLoaded || IsProxy) { return; } #region Outputs and Inputs and UI updating #region Find outputs // Find output elements for the node List <Output> outputs = WorkspaceModel.Nodes.OfType <Output>().ToList(); var topMost = new List <Tuple <int, NodeModel> >(); List <string> outNames; // if we found output nodes, add select their inputs // these will serve as the function output if (outputs.Any()) { topMost.AddRange( outputs.Where(x => x.HasInput(0)).Select(x => Tuple.Create(0, x as NodeModel))); outNames = outputs.Select(x => x.Symbol).ToList(); } else { outNames = new List <string>(); // if there are no explicitly defined output nodes // get the top most nodes and set THEM as the output IEnumerable <NodeModel> topMostNodes = WorkspaceModel.GetTopMostNodes(); var rtnPorts = //Grab multiple returns from each node topMostNodes.SelectMany( topNode => //If the node is a recursive instance... topNode is Function && (topNode as Function).Definition == this // infinity output ? new[] { new { portIndex = 0, node = topNode, name = "∞" } } // otherwise, grab all ports with connected outputs and package necessary info : topNode.OutPortData .Select( (port, i) => new { portIndex = i, node = topNode, name = port.NickName }) .Where(x => !topNode.HasOutput(x.portIndex))); foreach (var rtnAndIndex in rtnPorts.Select((rtn, i) => new { rtn, idx = i })) { topMost.Add(Tuple.Create(rtnAndIndex.rtn.portIndex, rtnAndIndex.rtn.node)); outNames.Add(rtnAndIndex.rtn.name ?? rtnAndIndex.idx.ToString()); } } var nameDict = new Dictionary <string, int>(); foreach (var name in outNames) { if (nameDict.ContainsKey(name)) { nameDict[name]++; } else { nameDict[name] = 0; } } nameDict = nameDict.Where(x => x.Value != 0).ToDictionary(x => x.Key, x => x.Value); outNames.Reverse(); var keys = new List <string>(); foreach (var name in outNames) { int amt; if (nameDict.TryGetValue(name, out amt)) { nameDict[name] = amt - 1; keys.Add(name == "" ? amt + ">" : name + amt); } else { keys.Add(name); } } keys.Reverse(); ReturnKeys = keys; #endregion //Find function entry point, and then compile var inputNodes = WorkspaceModel.Nodes.OfType <Symbol>().ToList(); var parameters = inputNodes.Select(x => string.IsNullOrEmpty(x.InputSymbol) ? x.AstIdentifierForPreview.Value: x.InputSymbol); Parameters = inputNodes.Select(x => x.InputSymbol); //Update existing function nodes which point to this function to match its changes var customNodeInstances = dynamoModel.AllNodes .OfType <Function>() .Where(el => el.Definition != null && el.Definition == this); foreach (var node in customNodeInstances) { node.ResyncWithDefinition(); } //Call OnSave for all saved elements foreach (var node in WorkspaceModel.Nodes) { node.OnSave(); } #endregion var outputNodes = topMost.Select((x) => { var n = x.Item2.GetAstIdentifierForOutputIndex(x.Item1); return(n as AssociativeNode); }); #if ENABLE_DYNAMO_SCHEDULER var initParams = new CompileCustomNodeParams() { EngineController = controller, Definition = this, Nodes = WorkspaceModel.Nodes.Where(x => !(x is Symbol)), Parameters = parameters, Outputs = outputNodes }; // Schedule the compilation of CustomNodeDefinition, we are // not interested in when it will be completed, so no callback. var scheduler = dynamoModel.Scheduler; var task = new CompileCustomNodeAsyncTask(scheduler); if (task.Initialize(initParams)) { scheduler.ScheduleForExecution(task); } #else controller.GenerateGraphSyncDataForCustomNode( this, WorkspaceModel.Nodes.Where(x => !(x is Symbol)), outputNodes, parameters); #endif // Not update graph until Run // if (success) // controller.UpdateGraph(); }