Python wrapped in custom node returns "Function"

I have a python node that work fine outside of a custom node. When I wrap the python node in a custom node, it just returns “Function” and doesn’t do anything.
image


What makes this script incompatible with wrapping it in a custom node? BTW…this is my first custom node.

import clr

# Add Assemblies for AutoCAD and Civil 3D APIs
clr.AddReference('acmgd')
clr.AddReference('acdbmgd')
clr.AddReference('accoremgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')
clr.AddReference('AeccPressurePipesMgd')
clr.AddReference('acdbmgdbrep')
clr.AddReference('System.Windows.Forms')
clr.AddReference('Civil3DNodes')
clr.AddReference('AutoCADNodes')

# Add standard Python references
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import os
import math

# Add references to manage arrays, collections and interact with the user
from System import *
from System.IO import *
from System.Collections.Specialized import *
from System.Windows.Forms import MessageBox

# Create an alias to the Autodesk.AutoCAD.ApplicationServices.Application class
import Autodesk.AutoCAD.ApplicationServices.Application as acapp

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references for Civil 3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

# Import references for Dynamo for Civil 3D
from Autodesk.Civil.DynamoNodes import Surface as DynSurface
from Autodesk.Civil.DynamoNodes.Selection import SurfaceByName
from Autodesk.AutoCAD.DynamoNodes import Document as DynDocument

adoc = acapp.DocumentManager.MdiActiveDocument
ed = adoc.Editor
civdoc = CivilApplication.ActiveDocument


def add_featurelines_links(fName, lName):
	"""
	This method adds feature lines and links to an existing corridor surface.
	:param Fnames: List of corridor feature lines to add
	:param Lnames: List of corridor links to add
	:return: nothing
	"""
	global adoc
	global ed
	global civdoc

	output = []

	# Get the active document in the AutoCAD session:
	with adoc.LockDocument():
		with adoc.Database as db:
			with db.TransactionManager.StartTransaction() as t:
				# Get the corridor and corridor surface
				corridorId = civdoc.CorridorCollection[corridorName]
				corridor = t.GetObject(corridorId, OpenMode.ForWrite)

				try:
					corridorSurface = corridor.CorridorSurfaces[corridorSurfaceName]

					# Add feature lines and links
					for n in fName:
						corridorSurface.AddFeatureLineCode(n)

					for n in lName:
						corridorSurface.AddLinkCode(n, True)

					if rebuild:
						corridor.Rebuild()

					t.Commit()
				except Exception() as ex:
					MessageBox.Show(ex.message)

	output.append(SurfaceByName(corridorSurfaceName, DynDocument.Current))

	return output
	
corridorName = IN[0]
corridorSurfaceName = IN[1]
fNames = IN[2]
lNames = IN[3]
rebuild = IN[4]

if not isinstance(fNames, list):
	fNames = [fNames]

if not isinstance(lNames, list):
	lNames = [lNames]

OUT = add_featurelines_links(fNames, lNames)

It appears you cannot set a default for a boolean input? This this correct? After I hooked a boolean to the node, it works.

image

Once I was playing with a custom node, I found out the code did not work as it did in the graph itself. It seems that you need to accept lists in a custom node.

Like this:

I don’t think the boolean is the problem.

I’m not sure what the issue is now. I added the list brackets to the inputs as you suggested. Still doesn’t work. It doesn’t output “Function” anymore, but now it outputs “null”. I wondering if has something to do with levels and/or lacing, but I have no idea how to address that for a python script wrapped in a custom node.

Hi @keith.sowinski

Possible to drop dyn and dwg file? I will try to solve your problem.

Thank you for your willingness to help. video explanation

AddFeatureLinesAndLinks.dyf (15.1 KB) CreateCorridorSurfaces.dyf (9.2 KB) CreateCorridorSurfaces2.dyn (68.3 KB) Crdr-DynSandbox.dwg (2.5 MB)

This works:

Featurelines and links are a list.

edit: apparently it does not work like expected. I’ll have a better look.

Check out my posted files for my latest attempt. I modified the python script some because it needs to be able to process multiple corridor surfaces. I’m still missing something though.

I’ll have a look later. It’s an interesting script so I am curious. No Python experience but I am a C# programmer so I understand what it does. Now dive into the mysteries of Custom Nodes and lacing.

@keith.sowinski Thanks for dropping the files. I was getting fatal error while opening your (dyn,dyf’s) files, so i recreated custom node using your python. Looks good to me:


Create_Surface_Add_Feature_Lines_Links.dyf (14.2 KB)

Hope it helps!

1 Like

Thank you for looking at this. Putting []..[] in the inputs worked. I am curious why you get a fatal error on open, but I don’t. I have follow-up questions.

  1. Are the inputs always supposed to include []..[], or only in certain situations? I tried just [], but that did not work. If I only expect a single item as an input (corridorName in this case), do I leave off the []..[]?
  2. This only seems to work if I have multiple surfaces with multiple lists of feature lines and links. If I only have a single surface, it does not work.
  3. If the list of feature lines and/or links is empty, it does not work.

edit: It does work with the empty lists.

Here you find more information about the brackets:

I thought you only add them where you expect a list (featurelines and links) but it seems not to work very well. It’s one of the mysteries I don’t understand yet, mainly because it works completely different if you put them in a Custom Node instead of directly in the graph.

1 Like

Thank you. That was very helpful.

I found that it will work for a single surface if I set the featurelines and links inputs to @L3. I would prefer to have the output list flattened, but haven’t figured that out yet. I’m sure I have something structurally incorrect, but it is functioning.

Multiple surfaces
image
Single surface
image

You can flatten the output in the Custom Node:

image

But if the Custom Node runs multiple times because of the replication, it will stil result in a list. You can flatten the output in your graph as well, then it is more flattened.

I’m not convinced the @L3 option is the solution. Will it work in any situation? One Corridor? Multiple? Or multiple featureline sets?

Or we can ask @solamour to explain how you can do this in a Custom Node :slight_smile:

I agree. I think there is something fundamentally wrong with how my python script is written.

I’m not sure this has anything to do with the customnode… I think it’s likely you should not call the civil nodes from code - they are likely written like the Dynamo Revit constructor nodes and use element binding which will do strange things when invoked from code…

thats just a guess though… try replacing your python with something simpler as a test.

1 Like

var[]..[] means - never replicate - the input passed to the custom node should not be replicated over, and will be passed into the custom node as is.

1 Like

Thank you for the suggestion. I tried removing the SurfaceByName call, but the results were the same.

The issue is that the feature line (and link) list structure is different when processing a single surface vs processing multiple surfaces. Trying to accommodate both scenarios. When there is a single surface, everything is as the same level. When there are multiple surfaces, there are sub lists for each surfaces’ feature lines.
image
So far, the solution is to set the feature line and link input levels to @L3 to get this structure for a single surface scenario. Alternatively, I can do it in advance as shown with this GetItemAtIndex node.
image
Maybe that’s just the way it is and I can communicate that to my users. If there is some way to handle this within the python script, that would be ideal.

@Anton_Huizinga @Kulkul @Michael_Kirschner2

Thank you for your help. I think I have something that is working. Here is my final python script. I ended up putting in a check for the corridor surface name input list length. If the length is 1, then I sublist the feature line and link lists. I also followed @Michael_Kirschner2 advice and pulled out the civil node call. I don’t think that was the issue in this case, but I did it as a precaution anyway. I’m just returning the corridor surface names instead (pass through). I also made sure to include []..[] in all of my inputs.

import clr

# Add Assemblies for AutoCAD and Civil 3D APIs
clr.AddReference('acmgd')
clr.AddReference('acdbmgd')
clr.AddReference('accoremgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')
clr.AddReference('AeccPressurePipesMgd')
clr.AddReference('acdbmgdbrep')
clr.AddReference('System.Windows.Forms')

# Add standard Python references
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import os
import math

# Add references to manage arrays, collections and interact with the user
from System import *
from System.IO import *
from System.Collections.Specialized import *
from System.Windows.Forms import MessageBox

# Create an alias to the Autodesk.AutoCAD.ApplicationServices.Application class
import Autodesk.AutoCAD.ApplicationServices.Application as acapp

# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *

# Import references for Civil 3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *

adoc = acapp.DocumentManager.MdiActiveDocument
ed = adoc.Editor
civdoc = CivilApplication.ActiveDocument


def add_featurelines_links(name, fName, lName):
	"""
	This method adds feature lines and links to an existing corridor surface.
	:param Fnames: List of corridor feature lines to add
	:param Lnames: List of corridor links to add
	:return: corridor surface name
	"""
	global adoc
	global ed
	global civdoc

	# Get the active document in the AutoCAD session:
	with adoc.LockDocument():
		with adoc.Database as db:
			with db.TransactionManager.StartTransaction() as t:
				# Get the corridor and corridor surface
				corridorId = civdoc.CorridorCollection[corridorName]
				corridor = t.GetObject(corridorId, OpenMode.ForWrite)
				if len(name) == 1:
					fName = [fName]
					lName = [lName]
				try:
					for n, f, l in zip(name, fName, lName):
						corridorSurface = corridor.CorridorSurfaces[n]

					# Add feature lines and links
						for i in f:
							corridorSurface.AddFeatureLineCode(i)
						for j in l:
							corridorSurface.AddLinkCode(j, True)

					if rebuild:
						corridor.Rebuild()

					t.Commit()
				except Exception() as ex:
					MessageBox.Show(ex.message)

	return corridorSurfaceName
	
corridorName = IN[0]
corridorSurfaceName = IN[1]
fNames = IN[2]
lNames = IN[3]
rebuild = IN[4]

if not isinstance(corridorSurfaceName, list):
	corridorSurfaceName = [corridorSurfaceName]

if not isinstance(fNames, list):
	fNames = [fNames]

if not isinstance(lNames, list):
	lNames = [lNames]

OUT = add_featurelines_links(corridorSurfaceName, fNames, lNames)

1 Like