I forked Assembly 4 and submitted a pull request on some easy bugs that I found with my test files.
https://github.com/Zolko-123/FreeCAD_Assembly4/compare/master...JonasThomas:FreeCAD_Assembly4:master
I ran into another here: https://github.com/JonasThomas/FreeCAD_Assembly4/blob/master/makeBomCmd.py#L303, that s easy enough to fix, but I don’t understand the complete picture at this point, and I thought better to ask before I start hacking away at the code.
PictureforForum.png
This is my situation:
BoltSpacerNutSubAssembly (Assembly) @ /home/jonasthomas/Documents/FreecadProjects/QuestionsReFreecad/Q_003-S.FCStd
├─ Constraints
├─ Configurations
├─ M8-Nut (Nut)
├─ M8-Washer (Washer)
├─ M8-Washer015 (Washer001)
├─ M8-Washer016 (Washer002)
├─ M8x40-Screw (Screw)
├─ M8-Washer014 (Washer003)
└─ PartSpacer => Spacer @ /home/jonasthomas/Documents/FreecadProjects/QuestionsReFreecad/Q_002-P.FCStd
└─ BodySpacer
In this test case, I have a customer spacer Q_003-S.FcStd that’s in a separate file for versioning control, that’s linked into the assembly.
def listParts(self, obj, level=0, parent=None):
file = open(ConfUserFilejson, 'r')
self.infoKeysUser = json.load(file).copy()
file.close()
max_level = 100
if self.follow_subassemblies == False:
max_level = 2;
if obj == None:
return
if self.PartsList == None:
self.PartsList = {}
#=======================
# VISIBLE APP LINK
#=======================
if obj.TypeId == 'App::Link':
if obj.Visibility == True:
print("ASM4> {level}{obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level), obj_label=obj.Label, obj_name=obj.FullName, obj_typeid=obj.TypeId))
# self.Verbose += "> {level} | {type}: {label}, {fullname}".format(level=obj.Label, type="APP_LINK", label=obj.Label, fullname=obj.FullName)
# try:
# self.Verbose += "- linked: {linked_obj}\n".format(linked_obj=obj.LinkedObject.Name)
# except:
# self.Verbose += "- linked: {linked_obj}\n".format(linked_obj=obj.Name)
# self.Verbose += '- not included\n\n'
# Navigate on objects inside a App:Links (Groups of Fastners)
if obj.ElementCount > 0:
for i in range(obj.ElementCount):
self.listParts(obj.LinkedObject, level, parent=obj)
else:
self.listParts(obj.LinkedObject, level + 1, parent=obj)
#==================================
# MODEL_PART aka ASM4 SUB-ASSEMBLY
#==================================
elif obj.TypeId == 'App::Part' and Asm4.isAsm4Model(obj):
if level > 0 and level <= max_level and self.follow_subassemblies == False:
# Recover the record, if any
try:
if self.infoKeysUser.get("Document").get('active'):
try:
doc_name = getattr(obj, self.infoKeysUser.get("Document").get('userData'))
except AttributeError:
doc_name = obj.Document.Name
except:
doc_name = obj.Document.Name
# Recover the record, if any
if self.infoKeysUser.get("Part_Label").get('active'):
try:
obj_label = getattr(obj, self.infoKeysUser.get("Part_Label").get('userData'))
except AttributeError:
obj_label = obj.Label
# The name cannot be Model othewise it will sum all other 'Model' names togueter
if obj_label == "Model":
obj_label = obj.Document.Name
if obj_label in self.PartsList:
if self.PartsList[obj_label]['Document'] == doc_name:
qtd = self.PartsList[obj_label]['Qty.'] + 1
print("ASM4> {level}| {qtd}x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId, qtd=qtd))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="ASM4_PART", label=obj_label, fullname=obj.FullName)
self.Verbose += "- object already added (" + str(qtd) + ")\n\n"
self.PartsList[obj_label]['Qty.'] = qtd
else:
print("ASM4> {level}| 1x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="ASM4_PART", label=obj_label, fullname=obj.FullName)
self.Verbose += "- adding object (1)\n"
self.PartsList[obj_label] = dict()
for prop in self.infoKeysUser:
if self.infoKeysUser.get(prop).get('active'):
try: # to get partInfo
getattr(obj, self.infoKeysUser.get(prop).get('userData'))
info = "(predefined)"
except AttributeError:
crea(self,obj)
fill(obj)
info = "(extracted)"
if self.infoKeysUser.get(prop).get('visible'):
data = getattr(obj, self.infoKeysUser.get(prop).get('userData'))
else:
data = "-"
if data == "":
data = "-"
if prop == "Part_Label":
data = obj_label
if data != "-":
self.Verbose += "- " + prop + ": " + data + " " + info + "\n"
self.PartsList[obj_label][self.infoKeysUser.get(prop).get('userData')] = data
self.PartsList[obj_label]['Qty.'] = 1
self.Verbose += '\n'
[b] #============================
# STANDALONE MODEL_PART
#============================[/b]
elif obj.TypeId == 'App::Part' and not Asm4.isAsm4Model(obj):
if level > 0 and level <= max_level:
obj_label = ""
# Recover the record, if any
try:
if self.infoKeysUser.get("Document").get('active'):
try:
doc_name = getattr(obj, self.infoKeysUser.get("Document").get('userData'))
except AttributeError:
doc_name = obj.Document.Name
except:
doc_name = obj.Document.Name
# Recover the record, if any
try:
if self.infoKeysUser.get("Part_Label").get('active'):
try:
obj_label = getattr(obj, self.infoKeysUser.get("Part_Label").get('userData'))
except AttributeError:
obj_label = obj.Label
except:
doc_name = obj.Label
# The name cannot be model othewise it will sum all other 'Model' names togueter
if obj_label == "Model":
obj_label = obj.Document.Name
if obj_label in self.PartsList:
if self.PartsList[obj_label]['Document'] == doc_name:
qtd = self.PartsList[obj_label]['Qty.'] + 1
print("ASM4> {level}| {qtd}x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId, qtd=qtd))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="PART", label=obj_label, fullname=obj.FullName)
self.Verbose += "- object already added (" + str(qtd) + ")\n\n"
self.PartsList[obj_label]['Qty.'] = qtd
else:
print("ASM4> {level}| 1x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="PART", label=obj_label, fullname=obj.FullName)
self.Verbose += "- adding object (1)\n"
self.PartsList[obj_label] = dict()
for prop in self.infoKeysUser:
if self.infoKeysUser.get(prop).get('active'):
try: # to get partInfo
getattr(obj, self.infoKeysUser.get(prop).get('userData'))
info = "(predefined)"
except AttributeError:
crea(self,obj)
fill(obj)
info = "(extracted)"
if self.infoKeysUser.get(prop).get('visible'):
data = getattr(obj, self.infoKeysUser.get(prop).get('userData'))
else:
data = "-"
if data == "":
data = "-"
if prop == "Part_Label":
data = obj_label
if data != "-":
[b] self.Verbose += "- " + prop + ": " + data + " " + info + "\n"[/b]
self.PartsList[obj_label][self.infoKeysUser.get(prop).get('userData')] = data
self.PartsList[obj_label]['Qty.'] = 1
self.Verbose += '\n'
#============================
# STANDALONE MODEL_PARTDESIGN
#============================
elif obj.TypeId == 'PartDesign::Body':
# if level > 0 and level <= max_level and Asm4.isAsm4Model(parent):
if level > 0 and level <= max_level :
##### This try/except isn't working right - Document field can't be set, but is active by default.
##### This results in a blank document name and the Quantity never increments on any part.
## Recover the record, if any
#try:
# if self.infoKeysUser.get("Document").get('active'):
# try:
# doc_name = getattr(obj, self.infoKeysUser.get("Document").get('userData'))
# except AttributeError:
# doc_name = obj.Document.Name
#except:
# doc_name = obj.Document.Name
doc_name = obj.Document.Name
if obj.Label in self.PartsList:
if self.PartsList[obj.Label]['Document'] == doc_name:
qtd = self.PartsList[obj.Label]['Qty.'] + 1
print("ASM4> {level}{qtd}x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj.Label, obj_name=obj.FullName, obj_typeid=obj.TypeId, qtd=qtd))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj.Label, type="PART", label=obj.Label, fullname=obj.FullName)
self.Verbose += "- object already added (" + str(qtd) + ")\n\n"
self.PartsList[obj.Label]['Qty.'] = qtd
else:
print("ASM4> {level}1x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj.Label, obj_name=obj.FullName, obj_typeid=obj.TypeId))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj.Label, type="PARTDESIGN", label=obj.Label, fullname=obj.FullName)
self.Verbose += "- adding object (1)\n"
self.PartsList[obj.Label] = dict()
for prop in self.infoKeysUser:
self.Verbose += "- " + prop + ': '
if prop == 'Document':
data = obj.Document.Label
elif prop == 'PartName':
data = obj.PartName
elif prop == 'PartLength':
data = obj.PartLength
elif prop == 'PartWidth':
data = obj.PartWidth
elif prop == 'PartHeight':
data = obj.PartHeight
else:
data = "-"
if data != "-":
self.Verbose += data + '\n'
self.PartsList[obj.Label][self.infoKeysUser.get(prop).get('userData')] = data
self.PartsList[obj.Label]['Qty.'] = 1
self.Verbose += '\n'
#============================
# FASTENERS AND ARRAYS
#============================
elif obj.TypeId == 'Part::FeaturePython' and (obj.Content.find("FastenersCmd") or (obj.Content.find("PCBStandoff")) > -1):
if level > 0 and level <= max_level:
doc_name = os.path.splitext(os.path.basename(obj.Document.FileName))[0]
obj_label = re.sub(r'[0-9]+$', '', obj.Label)
# if array
if obj.Content.find("Orthogonal array")>-1:
# count up the objects in the array
x_count = obj.NumberX
y_count = obj.NumberY
z_count = obj.NumberZ
total = x_count * y_count * z_count
# identify the linked object
subobj = obj.Base.LinkedObject
# count each instance of linked object
for i in range(0, total):
print(" ", i, "...")
self.listParts(subobj, level, parent=obj)
elif obj_label in self.PartsList:
if self.PartsList[obj_label]['Document'] == doc_name:
qtd = self.PartsList[obj_label]['Qty.'] + 1
print("ASM4> {level}| {qtd}x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId, qtd=qtd))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="FASTENER", label=obj_label, fullname=obj.FullName)
self.Verbose += "- object already added (" + str(qtd) + ")\n\n"
self.PartsList[obj_label]['Qty.'] = qtd
else: # if the part is a was not added already
print("ASM4> {level}| 1x | {obj_typeid} | {obj_name} | {obj_label}".format(level=self.indent(level, tag=" "), obj_label=obj_label, obj_name=obj.FullName, obj_typeid=obj.TypeId))
self.Verbose += "> {level} | {type}: {label}, {fullname}\n".format(level=obj_label, type="FASTENER", label=obj_label, fullname=obj.FullName)
self.Verbose += "- adding object (1)\n"
self.PartsList[obj_label] = dict()
for prop in self.infoKeysUser:
if prop == 'Document':
data = doc_name
elif prop == 'Part_Label':
data = obj_label
elif prop == "Fastener_Diameter":
data = obj.diameter
elif prop == "Fastener_Type":
data = obj.type
elif prop == "Fastener_Length":
try:
data = str(obj.length).strip("mm")
except:
data = ""
else:
data = "-"
if data != "-":
self.Verbose += "- " + prop + ': ' + data + '\n'
self.PartsList[obj_label][self.infoKeysUser.get(prop).get('userData')] = data
self.PartsList[obj_label]['Qty.'] = 1
self.Verbose += '\n'
# else:
# print("@", obj.TypeId)
#===================================
# Continue walking inside the groups
#===================================
# Navigate on objects inide a folders
if obj.TypeId == 'App::DocumentObjectGroup':
for objname in obj.getSubObjects():
subobj = obj.Document.getObject(objname[0:-1])
self.listParts(subobj, level, parent=obj)
# Navigate on objects inide a ASM4 Part (Links and Folders)
if obj.TypeId == 'App::Part':
for objname in obj.getSubObjects():
subobj = obj.Document.getObject(objname[0:-1])
# if subobj.TypeId == 'App::Link' or subobj.TypeId == 'App::DocumentObjectGroup':
self.listParts(subobj, level+1, parent=obj)
return
self.Verbose += '\nBOM creation is done\n'
The issue is in this line.
self.Verbose += "- " + prop + ": " + data + " " + info + “\n”
The problem is that it was a document object and it was expecting text.
As I mentioned, easy enough get past the error, but it feels like there is more code handling that needs to take place and I’m just starting understand what’s going on here. If someone with more familiarity with whats going on here could chime it. It would be appreciated.
Edit.
I added this to get past the error. But I just feels it feels like there should be more code here:
if prop == "Part_Label":
data = obj_label
if data != "-":
try:
self.Verbose += "- " + prop + ": " + data + " " + info + "\n"
e[b]xcept TypeError as e:
self.Verbose += "- Error: " + str(e) + "\n"
#todo Not sure if this is the best solution.[/b]
self.PartsList[obj_label][self.infoKeysUser.get(prop).get('userData')] = data
self.PartsList[obj_label]['Qty.'] = 1
self.Verbose += '\n'
Edit #2 After fixing? this error, the next error seems to be recursion related.. Need to step away and walk the dog for a while.