With the latest Fusion 360 release, available starting today, you will see a huge increase in the APIs available for the manufacturing workspace. This is a massive release, that I know many of you have been waiting for!
See here for a comprehensive list of these new capabilities.
You'll notice that all of the new Manufacture functionality is designated as being in "Preview". We're being cautious and releasing it as a preview, so we can react to problems and suggestions for improvement and be open to making changes that might break compatibility.
Please test it out and let us know in this thread:
Depending on the feedback we get, the plan is to take it out of preview after one or two major releases.
I am using templates to do some awesome automation using these new API features. I am having a bit of an issue though. When I use a template and calculate all of the toolpaths, if the operation is a rest material operation and no toolpath is found, further operations down the line are compromised with the warning: "failed to generate rest material". I don't think this issue is exclusive to building operations using the API though, and also occurs during manual creation. Is there anyway to get around this issue?
The only work around I see right now is that instead of generating a collection of operations, or an entire setup, loop through each operation and generate it, then check if it has toolpath, and delete it if it doesn't. The only real issue with this is that this forces me to do each operation one at a time instead of calculating all of them at once.
@Joshua.mursic - Can you share the script you're using so we can take a closer look with it?
Thanks,
Jeff
Its part of a large addon that is heavily configured to work on my computer, I will try to build a script that isolates the error this weekend. Thanks for the fast response
You could instantiate a whole bunch of templated operations and generate them all at once but then go through them and perform cleanup, generate, repeat - until your setup has toolpaths on all operations.
Not sure if the available events help you do this clean enough though.
This is exactly what I ended up doing, thanks for the suggestion. Heres the code I came up with.
def toolpathStatus(self,generate:adsk.cam.GenerateToolpathFuture,cleanUp=False):
'''
generate: cam.generateToolpath(setup)
'''
cam:adsk.cam.CAM = app.activeProduct
# create and show the progress dialog while the toolpaths are being generated.
progress = ui.createProgressDialog()
progress.isCancelButtonShown = True
progress.show(f'Calculating Toolpath', '', 0, generate.numberOfOperations)
# Enter a loop to wait while the toolpaths are being generated and update the progress dialog.
while generate.isGenerationCompleted is False:
generate.numberOfCompleted
progress.progressValue = generate.numberOfCompleted
if cleanUp == False: progress.message = f'Calculating Operations {generate.numberOfCompleted} of {generate.numberOfOperations}'
if cleanUp == True: progress.message = f'Cleaning Up Operations {generate.numberOfCompleted} of {generate.numberOfOperations}'
progress.title = f"Generating Toopath"
if progress.wasCancelled:
return "Stop"
adsk.doEvents()
#Check if the any operations have empty toolpath
regenerateSetup = []
for operation in generate.operations:
if operation.hasWarning and "Empty toolpath" in operation.warning:
if operation.parentSetup not in regenerateSetup:
regenerateSetup.append(operation.parentSetup)
app.log(f'Operation: {operation.name}. Empty toolpath.')
app.log(f' ')
operation.deleteMe()
#Loop through setups that had operations deleted becasue they were empty and recalculate the affected dependant ops
for setup in regenerateSetup:
operationsToRecalculate = adsk.core.ObjectCollection.create()
for operation in setup.operations:
if operation.isToolpathValid is False:
operationsToRecalculate.add(operation)
if operationsToRecalculate.count > 0:
cleanUpGenerate = cam.generateToolpath(operationsToRecalculate)
self.toolpathStatus(cleanUpGenerate,True)
progress.hide()
There are a couple of bugs I have found:
First when trying to use parts of a component that is an external reference, the CadObjectParamater Input is rejected and left empty. I posted about it here. When the link is broken on the same item so it is no longer an external reference, the parts can be used for input.
Also, we don't seem to be able to change the parameters of the postprocessor. When we try to get the parameter inside to post processor we can read it but cannot change its value.
def post(setup):
ncInput = cam.ncPrograms.createInput()
ncInput.operations = list(setup.operations)
newProgram = cam.ncPrograms.add(ncInput)
#Get the post configuration
camManager = adsk.cam.CAMManager.get()
libraryManager = camManager.libraryManager
postLibraries = libraryManager.postLibrary
cloudFolder = postLibraries.urlByLocation(adsk.cam.LibraryLocations.CloudLibraryLocation)
cloudPostlibrary = postLibraries.childAssetURLs(cloudFolder)
# Get Templates
for cloudPost in cloudPostlibrary:
postName = cloudPost.toString()
if "test_post.cps" in postName:
postUrl = adsk.core.URL.create(postName)
# postConfiguration = postLibraries.postConfigurationAtURL(postUrl)
# return postUrl.toString()
postConfig = postLibraries.postConfigurationAtURL(postUrl)
# Error if not found
ui.messageBox(f"Could not find Postprocessor 'test_post.cps'")
newProgram.postConfiguration = postConfig
#Try to change the post parameters
newProgram.postParameters.itemByName('coolant_Toggle').value.value = False
newProgram.postParameters.itemByName('coolant_Toggle').expression = "false"
#post the program
postOptions = adsk.cam.NCProgramPostProcessOptions.create()
postSuccessful = newProgram.postProcess(postOptions)
It seems that "job_stockMode" in adsk.cam.SetupInput.parameters is not correctly reflected.
The following script is meaningless, it only creates multiple setups, but I have it create the stock settings as shown in the image below.
# Fusion360API Python script
import traceback
import adsk.core as core
import adsk.cam as cam
def run(context):
ui: core.UserInterface = None
try:
app: core.Application = core.Application.get()
ui = app.userInterface
camObj: cam.CAM = get_cam_product()
# create setup
for _ in range(3):
create_setup(
camObj,
)
except:
if ui:
ui.messageBox("Failed:\n{}".format(traceback.format_exc()))
def create_setup(
camObj: cam.CAM,
) -> None:
setups: cam.Setups = camObj.setups
setupIpt: cam.SetupInput = setups.createInput(
cam.OperationTypes.MillingOperation
)
set_cam_parameter(
setupIpt,
'job_stockMode',
'default',
)
set_cam_parameter(
setupIpt,
'job_stockOffsetMode',
'keep',
)
setups.add(setupIpt)
def set_cam_parameter(
camEntity,
name: str,
value: str,
) -> bool:
try:
prm: cam.CAMParameter = camEntity.parameters.itemByName(
name
)
if not prm: return False
prm.value.value = value
return True
except:
return False
def get_cam_product() -> cam.CAM:
app: core.Application = core.Application.get()
activete_cam_env()
return app.activeProduct
def activete_cam_env() -> None:
app: core.Application = core.Application.get()
ui: core.UserInterface = app.userInterface
camWS: core.Workspace = ui.workspaces.itemById('CAMEnvironment')
camWS.activate()
When the new document is created and executed, the first setup is set as desired, but the second and subsequent setups are set to "From preceding setup".
However, you can change the create_setup function as follows
If you change the parameters after adding setups.add, you will get the correct result.
def create_setup(
camObj: cam.CAM,
) -> None:
setups: cam.Setups = camObj.setups
setupIpt: cam.SetupInput = setups.createInput(
cam.OperationTypes.MillingOperation
)
setup: cam.Setup = setups.add(setupIpt)
set_cam_parameter(
setup,
'job_stockMode',
'default',
)
set_cam_parameter(
setup,
'job_stockOffsetMode',
'keep',
)
The hole recognition sample here has multiple errors and cannot be run as is.
https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-7249049A-3CC8-489C-9187-D9960EB07607
Here is a corrected version that is error-free.
import adsk.core, adsk.fusion, adsk.cam, traceback
_app: adsk.core.Application = adsk.core.Application.get()
_ui:adsk.core.UserInterface = _app.userInterface
def run(context):
try:
# Get the first body in the root component.
des: adsk.fusion.Design = _app.activeDocument.products.itemByProductType('DesignProductType')
body = des.rootComponent.bRepBodies[0]
# Recognize the holes in the body.
holeGroups = adsk.cam.RecognizedHoleGroup.recognizeHoleGroups([body])
_app.log(f"Hole Group count: {holeGroups.count}")
# Print out information about the found holes in the TEXT COMMAND window in Fusion.
holeGroup: adsk.cam.RecognizedHoleGroup
for holeGroup in holeGroups:
# Process each hole within this group.
_app.log(f" Holes in Group count: {holeGroup.count}")
hole: adsk.cam.RecognizedHole
for hole in holeGroup:
_app.log(f" Segments in hole: {hole.segmentCount}")
for i in range(hole.segmentCount):
# Display information about each segment of the current hole.
segment: adsk.cam.RecognizedHoleSegment = hole.segment(i)
_app.log(f' Segment {i}')
if segment.holeSegmentType == adsk.cam.HoleSegmentType.HoleSegmentTypeCone:
_app.log(f' Segment Type: Cone')
elif segment.holeSegmentType == adsk.cam.HoleSegmentType.HoleSegmentTypeFlat:
_app.log(f' Segment Type: Flat')
elif segment.holeSegmentType == adsk.cam.HoleSegmentType.HoleSegmentTypeCylinder:
_app.log(f' Segment Type: Cylinder')
elif segment.holeSegmentType == adsk.cam.HoleSegmentType.HoleSegmentTypeTorus:
_app.log(f' Segment Type: Torus')
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Also, I feel that the pocket recognition image does not match the description.
Did this fix make it into the September release?
Scott Moyse
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
RevOps Strategy Manager at Toolpath. New Zealand based.
Co-founder of the Grumpy Sloth full aluminium billet mechanical keyboard project
Does anyone have a list of the available CAM events we can track? I'm hoping for events like:
or events to that effect.
Scott Moyse
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
RevOps Strategy Manager at Toolpath. New Zealand based.
Co-founder of the Grumpy Sloth full aluminium billet mechanical keyboard project
Hello Fusion 360 Community,
The operations created using createFromCAMTemplate2 are added to the end of the parent setup. However, I have a requirement to add them to the start of the setup. I've searched through the documentation but couldn't find a direct way to change the sequence of operations within a setup.
Is there a specific method or property that allows me to control the sequence of operations in a CAM setup? Or is there an alternative approach to achieve this?
Any insights or guidance on this matter would be greatly appreciated. Thank you in advance for your time and assistance!
There are new methods allowing you to do that, they're just not showing up in the documentation yet. All of those methods are on the OperationBase class and should be available to you already:
- moveBefore(OperationBase targetOperationBase)
- moveAfter(OperationBase targetOperationBase)
- moveInto(OperationBase targetContainer) // i.e. a Setup or CAMFolder
Related to the above, the following have also been added:
- copyBefore(OperationBase targetOperationBase)
- copyAfter(OperationBase targetOperationBase)
- copyInto(OperationBase targetContainer) // i.e. a Setup or CAMFolder
I have v 2.0.18460 of API libraries and don't have these methods. How I can update the libraries?
The methods do not auto complete in VS Code yet. The below snippet should work, just tried it on 2.0.18460 on my machine.
import os.path, sys
import adsk.core, adsk.fusion, adsk.cam, traceback
def run(context):
ui = None
try:
app = adsk.core.Application.get()
doc = app.activeDocument
ui = app.userInterface
ui.messageBox("This script needs a project with atleast 2 setups and 2 operations in each setup")
products = doc.products
cam = adsk.cam.CAM.cast(products.itemByProductType("CAMProductType"))
setup1 = cam.setups[0]
setup2 = cam.setups[1]
operation11 = setup1.operations[0]
operation12 = setup1.operations[1]
operation21 = setup2.operations[0]
operation11.moveAfter(operation21)
operation21.moveBefore(operation12)
operation12.moveInto(setup2)
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Hi @dames123 -San.
This is a topic for reporting bugs, etc., so if this is a question, it would be better to create a new topic.
A sample NC program was created for each setup.
# Fusion360API Python script
import traceback
import adsk.core as core
import adsk.fusion as fusion
import adsk.cam as cam
def run(context):
ui: core.UserInterface = None
try:
app: core.Application = core.Application.get()
ui = app.userInterface
camObj: cam.CAM = app.activeProduct
# https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-995d634e-a8ef-4f2b-a826-1d32171f4998
ncPrograms: cam.NCPrograms = camObj.ncPrograms
for setup in camObj.setups:
ncIpt: cam.NCProgramInput = ncPrograms.createInput()
ncIpt.displayName = setup.name
ncParameters = ncIpt.parameters
ncParameters.itemByName('nc_program_filename').value.value = setup.name
ncParameters.itemByName('nc_program_openInEditor').value.value = True
ncIpt.operations = [setup]
ncPrograms.add(ncIpt)
ui.messageBox("Done")
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Since this is only a minimum setup, I think it is necessary to make more settings for cps files, post properties, etc. in practice.
Hello,
A future-request:
Now, I can't not one of all the "avoid/machine surfaces" values in a advanced swarf operation over the API.
For example have all offset values the same parameter-Name ("clearance"), and not one is changed, if I set this over the API. Also all other parameters in this field can't be adjusted over the API.
(please see pic).
Best regards
Maurizio
Can't find what you're looking for? Ask the community or share your knowledge.