Demonstrates creating a setup, searching tool library to retrieve a tool, create a couple of machining operations and a NC program, ready for post processing.
Use the 2D Strategies model from the Fusion CAM Samples folder as your CAD model.
#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <Cam/CamAll.h>
#include <chrono>
#include <thread>
#include <algorithm>
#ifdef _WINDOWS
#include <shlwapi.h>
#else
#include <stdlib.h>
#endif
using namespace adsk::core;
using namespace adsk::fusion;
using namespace adsk::cam;
extern "C" XI_EXPORT bool run(const char* context)
{
// Initialisation
Ptr<Application> app = Application::get();
Ptr<UserInterface> ui = app->userInterface();
Ptr<Document> doc = nullptr;
// Load by URN a specific sample project to demonstrate a basic milling workflow.
std::string urn = "urn:adsk.wipprod:fs.file:vf.KoBHzV4mTOiNvFStiBwpzA?version=1"; // Production
// Load 2D Strategies model from the Fusion CAM Samples folder
Ptr<DataFile> sampleFile = app->data()->findFileById(urn);
if (sampleFile)
{
doc = app->documents()->open(sampleFile, true);
}
// Did we find our document?
if (!doc)
{
ui->messageBox("Sample file not found, using the current document.", "", MessageBoxButtonTypes::OKButtonType);
// Use existing document, load 2D Strategies model from the Fusion CAM Samples folder
doc = app->activeDocument();
}
// Switch to manufacturing space
Ptr<Workspace> camWS = ui->workspaces()->itemById("CAMEnvironment");
camWS->activate();
Ptr<Products> products = doc->products();
if (!products)
return false;
// Check if the document has a CAMProductType. It will return if there are no CAM operations in it.
Ptr<CAM> camProduct = products->itemByProductType("CAMProductType");
if (!camProduct)
{
ui->messageBox(
"There are no CAM operations in the active document. This script requires the active document to contain "
"at least one CAM operation.",
"No CAM Operations Exist",
MessageBoxButtonTypes::OKButtonType,
MessageBoxIconTypes::CriticalIconType);
return false;
}
// Find tools in sample tool library
// Get the tool libraries from the library manager
Ptr<CAMManager> camManager = CAMManager::get();
Ptr<CAMLibraryManager> libraryManager = camManager->libraryManager();
Ptr<ToolLibraries> toolLibraries = libraryManager->toolLibraries();
// We can use a library URl directly if we know its address (here we use Fusion"s Metric sample library)
Ptr<URL> toolLibraryUrl = URL::create("systemlibraryroot://Samples/Milling Tools (Metric).json");
// Load tool library
Ptr<ToolLibrary> toolLibrary = toolLibraries->toolLibraryAtURL(toolLibraryUrl);
if (!toolLibrary)
{
ui->messageBox("Failed to load tool library");
return false;
}
// Create some variables for the milling tools which will be used in the operations
Ptr<Tool> faceTool = nullptr;
Ptr<Tool> adaptiveTool = nullptr;
// Searching the face mill and the bull nose using a loop for the roughing operations
for (Ptr<Tool>& tool : toolLibrary)
{
// Read the tool type
Ptr<ChoiceParameterValue> toolTypeParameter = tool->parameters()->itemByName("tool_type")->value();
std::string toolType = toolTypeParameter->value();
// Select the first face tool found
if (toolType == "face mill" && !faceTool)
{
faceTool = tool;
}
// Search the roughing tool
else if (toolType == "bull nose end mill" && !adaptiveTool)
{
// We look for a bull nose end mill tool larger or equal to 10mm but less than 14mm
Ptr<FloatParameterValue> diameterParameter = tool->parameters()->itemByName("tool_diameter")->value();
double diameter = diameterParameter->value();
if (diameter >= 1.0 && diameter < 1.4)
{
adaptiveTool = tool;
}
}
// Exit when the 2 tools are found
if (faceTool && adaptiveTool)
{
break;
}
}
if (!faceTool)
{
ui->messageBox("No face mill tool found");
return false;
}
if (!adaptiveTool)
{
ui->messageBox("No bull nose end mill tool found");
return false;
}
// Create setup
Ptr<CAM> cam = products->itemByProductType("CAMProductType");
Ptr<Setups> setups = cam->setups();
Ptr<SetupInput> setupInput = setups->createInput(OperationTypes::MillingOperation);
// Create a list for the models to add to the setup Input
std::vector<Ptr<Base>> models;
Ptr<BRepBody> part = cam->designRootOccurrence()->bRepBodies()->item(0);
if (!part)
{
ui->messageBox(
"No part found in the current document, exiting sample script.", "", MessageBoxButtonTypes::OKButtonType);
return false;
}
// Add the part to the model list
models.push_back(part);
// Pass the model list to the setup input
setupInput->models(models);
// Create the setup
Ptr<Setup> setup = setups->add(setupInput);
// Change some properties of the setup
setup->name() = "CAM Basic Script Sample";
setup->stockMode(SetupStockModes::RelativeBoxStock);
// Set offset mode
setup->parameters()->itemByName("job_stockOffsetMode")->expression("'simple'");
// Set offset stock side
setup->parameters()->itemByName("job_stockOffsetSides")->expression("0 mm");
// Set offset stock top
setup->parameters()->itemByName("job_stockOffsetTop")->expression("2 mm");
// Set setup origin
Ptr<ChoiceParameterValue> wcs_origin_boxPoint = setup->parameters()->itemByName("wcs_origin_boxPoint")->value();
wcs_origin_boxPoint->value("top 1");
// Face operation
// Create a face operation input
Ptr<OperationInput> faceInput = setup->operations()->createInput("face");
faceInput->tool(faceTool);
faceInput->displayName("Face Operation");
faceInput->parameters()->itemByName("tolerance")->expression("0.01 mm");
faceInput->parameters()->itemByName("stepover")->expression("0.75 * tool_diameter");
faceInput->parameters()
->itemByName("direction")
->expression("'climb'"); // Must be single quotes inside double quotes
// Add the operation to the setup
Ptr<Operation> faceOp = setup->operations()->add(faceInput);
// Adaptive operation
Ptr<OperationInput> adaptiveInput = setup->operations()->createInput("adaptive");
adaptiveInput->tool(adaptiveTool);
adaptiveInput->displayName("Adaptive Roughing");
adaptiveInput->parameters()->itemByName("tolerance")->expression("0.1 mm");
adaptiveInput->parameters()->itemByName("maximumStepdown")->expression("5 mm");
adaptiveInput->parameters()->itemByName("fineStepdown")->expression("0.25 * maximumStepdown");
adaptiveInput->parameters()->itemByName("flatAreaMachining")->expression("false");
// Add the operation to the setup
Ptr<Operation> adaptiveOp = setup->operations()->add(adaptiveInput);
// Generate operations
cam->generateToolpath(faceOp);
Ptr<GenerateToolpathFuture> genFuture = cam->generateToolpath(adaptiveOp);
// ncProgram and post-processing
// Get the post library from library manager
Ptr<PostLibrary> postLibrary = libraryManager->postLibrary();
// Query post library to get postprocessor list
Ptr<PostConfigurationQuery> postQuery = postLibrary->createQuery(LibraryLocations::Fusion360LibraryLocation);
postQuery->vendor("Autodesk");
postQuery->capability(PostCapabilities::Milling);
std::vector<Ptr<PostConfiguration>> postConfigs = postQuery->execute();
Ptr<URL> importedURL;
std::string ncExtension;
// Find the "XYZ" post in the post library and import it to local library
for (Ptr<PostConfiguration>& config : postConfigs)
{
if (config->description() == "XYZ")
{
ncExtension = config->extension();
Ptr<URL> configUrl = URL::create("user://");
importedURL = postLibrary->importPostConfiguration(config, configUrl, "NCProgramSamplePost.cps");
break;
}
}
// Get the imported local post config
Ptr<PostConfiguration> postConfig = postLibrary->postConfigurationAtURL(importedURL);
// Create NCProgramInput object
Ptr<NCProgramInput> ncInput = cam->ncPrograms()->createInput();
ncInput->displayName("NC Program Sample");
// Change some nc program parameters...
Ptr<CAMParameters> ncParameters = ncInput->parameters();
Ptr<StringParameterValue> nc_program_filename = ncParameters->itemByName("nc_program_filename")->value();
nc_program_filename->value("NCProgramSample");
Ptr<BooleanParameterValue> nc_program_openInEditor = ncParameters->itemByName("nc_program_openInEditor")->value();
nc_program_openInEditor->value(true);
// Set temp directory as output directory
std::string outputFolder = cam->temporaryFolder();
// Variable outputFolder may contain backslashes but nc_program_output_folder requires forward slashes
std::replace_if(
outputFolder.begin(), outputFolder.end(), [](char ch) { return ch == '\\'; }, '/');
Ptr<StringParameterValue> nc_program_output_folder = ncParameters->itemByName("nc_program_output_folder")->value();
nc_program_output_folder->value(outputFolder);
// Select the operations to generate (we skip steep_and_shallow here)
ncInput->operations({faceOp, adaptiveOp});
// Add a new ncprogram from the ncprogram input
Ptr<NCProgram> newProgram = cam->ncPrograms()->add(ncInput);
// Set post processor
newProgram->postConfiguration(postConfig);
// Change some post parameter
Ptr<CAMParameters> postParameters = newProgram->postParameters();
// NcProgram parameters are passed without units to the postprocessor
Ptr<FloatParameterValue> builtin_tolerance = postParameters->itemByName("builtin_tolerance")->value();
builtin_tolerance->value(0.01);
Ptr<FloatParameterValue> builtin_minimumChordLength =
postParameters->itemByName("builtin_minimumChordLength")->value();
builtin_minimumChordLength->value(0.33);
// Update/apply post parameters
newProgram->updatePostParameters(postParameters);
// Post-process
// Set post options, by default post process only valid operations containing toolpath data
Ptr<NCProgramPostProcessOptions> postOptions = NCProgramPostProcessOptions::create();
// Ensure toolpaths are visible
faceOp->isLightBulbOn(true);
adaptiveOp->isLightBulbOn(true);
// To avoid errors, post-process only when toolpath generation has completed
if (!genFuture)
return false;
while (!genFuture->isGenerationCompleted())
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
newProgram->postProcess(postOptions);
// Advise where the NC file is located to indicate completion
ui->messageBox(
"The result has been written to:\n" + outputFolder + "/" + nc_program_filename->value() + ncExtension,
"Post processing is complete \t\t\t\t\t");
return true;
}
import adsk.core, adsk.fusion, adsk.cam, traceback
import time
def run(context):
ui = None
try:
#################### Initialisation #####################
app = adsk.core.Application.get()
ui = app.userInterface
PROJECT_URN = 'urn:adsk.wipprod:fs.file:vf.KoBHzV4mTOiNvFStiBwpzA?version=1' # Production
# Load by URN a specific sample project to demonstrate a basic milling workflow.
doc = loadProjectFromURN(PROJECT_URN)
if doc is None:
return
# Switch to manufacturing space
camWS: adsk.core.Workspace = ui.workspaces.itemById('CAMEnvironment')
camWS.activate()
# Get the CAM product
products: adsk.core.Products = doc.products
#################### Find tools in sample tool library ####################
# Get the tool libraries from the library manager
camManager = adsk.cam.CAMManager.get()
libraryManager: adsk.cam.CAMLibraryManager = camManager.libraryManager
toolLibraries: adsk.cam.ToolLibraries = libraryManager.toolLibraries
# We can use a library URl directly if we know its address (here we use Fusion's Metric sample library)
url = adsk.core.URL.create('systemlibraryroot://Samples/Milling Tools (Metric).json')
# Load tool library
toolLibrary: adsk.cam.ToolLibrary = toolLibraries.toolLibraryAtURL(url)
# Create some variables for the milling tools which will be used in the operations
faceTool: adsk.cam.Tool = None
adaptiveTool: adsk.cam.Tool = None
# Searching the face mill and the bull nose using a loop for the roughing operations
for tool in toolLibrary:
# Read the tool type
toolType = tool.parameters.itemByName('tool_type').value.value
# Select the first face tool found
if toolType == 'face mill' and not faceTool:
faceTool = tool
# Search the roughing tool
elif toolType == 'bull nose end mill' and not adaptiveTool:
# We look for a bull nose end mill tool larger or equal to 10mm but less than 14mm
diameter = tool.parameters.itemByName('tool_diameter').value.value
if diameter >= 1.0 and diameter < 1.4:
adaptiveTool = tool
# Exit when the 2 tools are found
if faceTool and adaptiveTool:
break
#################### Create setup ####################
cam: adsk.cam.CAM = adsk.cam.CAM.cast(products.itemByProductType("CAMProductType"))
setups: adsk.cam.Setups = cam.setups
setupInput: adsk.cam.SetupInput = setups.createInput(adsk.cam.OperationTypes.MillingOperation)
# Create a list for the models to add to the setup Input
models = []
# Identify the part or exit gracefully
try:
part: adsk.fusion.BRepBody = cam.designRootOccurrence.bRepBodies.item(0)
except Exception as e:
ui.messageBox('No part found in the current document, exiting sample script.')
return
# Add the part to the model list
models.append(part)
# Pass the model list to the setup input
setupInput.models = models
# Create the setup
setup: adsk.cam.Setup = setups.add(setupInput)
# Change some properties of the setup
setup.name = 'CAM Basic Script Sample'
setup.stockMode = adsk.cam.SetupStockModes.RelativeBoxStock
# Set offset mode
setup.parameters.itemByName('job_stockOffsetMode').expression = "'simple'"
# Set offset stock side
setup.parameters.itemByName('job_stockOffsetSides').expression = '0 mm'
# Set offset stock top
setup.parameters.itemByName('job_stockOffsetTop').expression = '2 mm'
# Set setup origin
setup.parameters.itemByName('wcs_origin_boxPoint').value.value = 'top 1'
#################### Face operation ####################
# Create a face operation input
faceInput: adsk.cam.OperationInput = setup.operations.createInput('face')
faceInput.tool = faceTool
faceInput.displayName = 'Face Operation'
faceInput.parameters.itemByName('tolerance').expression = '0.01 mm'
faceInput.parameters.itemByName('stepover').expression = '0.75 * tool_diameter'
faceInput.parameters.itemByName('direction').expression = "'climb'"
# Add the operation to the setup
faceOp: adsk.cam.OperationBase = setup.operations.add(faceInput)
#################### Adaptive operation ####################
adaptiveInput = setup.operations.createInput('adaptive')
adaptiveInput.tool = adaptiveTool
adaptiveInput.displayName = 'Adaptive Roughing'
adaptiveInput.parameters.itemByName('tolerance').expression = '0.1 mm'
adaptiveInput.parameters.itemByName('maximumStepdown').expression = '5 mm'
adaptiveInput.parameters.itemByName('fineStepdown').expression = '0.25 * maximumStepdown'
adaptiveInput.parameters.itemByName('flatAreaMachining').expression = 'false'
# Add the operation to the setup
adaptiveOp: adsk.cam.OperationBase = setup.operations.add(adaptiveInput)
##################### Generate operations ####################
cam.generateToolpath(faceOp)
genFuture: adsk.cam.GenerateToolpathFuture = cam.generateToolpath(adaptiveOp)
#################### ncProgram and post-processing ####################
# Get the post library from library manager
postLibrary: adsk.cam.PostLibrary = libraryManager.postLibrary
# Query post library to get postprocessor list
libraryLocation = adsk.cam.LibraryLocations.Fusion360LibraryLocation
postQuery: adsk.cam.PostConfigurationQuery = postLibrary.createQuery(libraryLocation)
postQuery.vendor = "Autodesk"
postQuery.capability = adsk.cam.PostCapabilities.Milling
postConfigs: list[adsk.cam.PostConfiguration] = postQuery.execute()
# Find the "XYZ" post in the post library and import it to local library
ncExtension = ''
for config in postConfigs:
if config.description == 'XYZ':
ncExtension = config.extension
url = adsk.core.URL.create("user://")
importedURL = postLibrary.importPostConfiguration(config, url, "NCProgramSamplePost.cps")
# Get the imported local post config
postConfig: adsk.cam.PostConfiguration = postLibrary.postConfigurationAtURL(importedURL)
# Create NCProgramInput object
ncInput: adsk.cam.NCProgramInput = cam.ncPrograms.createInput()
ncInput.displayName = 'NC Program Sample'
# Change some nc program parameters...
ncFilename = 'NCProgramSample'
ncParameters: adsk.cam.CAMParameters = ncInput.parameters
ncParameters.itemByName('nc_program_filename').value.value = ncFilename
ncParameters.itemByName('nc_program_openInEditor').value.value = True
ncParameters.itemByName('nc_program_nc_extension').value.value = ncExtension
# Set temp directory as output directory
# Make the path valid for Fusion by replacing \\ to / in the path
outputFolder = str(cam.temporaryFolder).replace('\\', '/')
ncParameters.itemByName('nc_program_output_folder').value.value = outputFolder
# Select the operations to generate
ncInput.operations = [faceOp, adaptiveOp]
# Add a new ncprogram from the ncprogram input
newProgram: adsk.cam.NCProgram = cam.ncPrograms.add(ncInput)
# Set post processor
newProgram.postConfiguration = postConfig
# Change some post parameters
postParameters: adsk.cam.CAMParameters = newProgram.postParameters
# NcProgram parameters are passed without units to the postprocessor
postParameters.itemByName('builtin_tolerance').value.value = 0.01
postParameters.itemByName('builtin_minimumChordLength').value.value = 0.33
# Update/apply post parameters
newProgram.updatePostParameters(postParameters)
# Post-process
# Set post options, by default post process only valid operations containing toolpath data
postOptions = adsk.cam.NCProgramPostProcessOptions.create()
# Ensure toolpaths are visible
faceOp.isLightBulbOn = True
adaptiveOp.isLightBulbOn = True
# To avoid errors, post-process only when toolpath generation has completed
while not genFuture.isGenerationCompleted:
time.sleep(1)
newProgram.postProcess(postOptions)
# Advise where the NC file is located to indicate completion
ui.messageBox(f'The results have been written to:\n{outputFolder}/{ncFilename}{ncExtension}',
'Post processing is complete \t\t\t\t\t'); # Prevent line breaks of the pathname
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def loadProjectFromURN(urn:str = None) -> adsk.core.Document:
''' Minimal self-contained function to load and return a document via URN or return None safely '''
doc: adsk.core.Document = None
app = adsk.core.Application.get()
if urn is not None:
try: # File not found causes an exception
project: adsk.core.DataFile = app.data.findFileById(urn)
if project:
doc = app.documents.open(project, True)
else:
app.userInterface.messageBox(f'File not found for URN: {urn}!')
except Exception as e:
if str(e)[0:38] == '3 : Design is located in another team.':
# Although the document has been loaded, variable 'doc' may not be populated
if doc is None:
doc: adsk.core.Document = adsk.core.Application.get().activeDocument
elif str(e)[0:20] == '3 : file not found':
app.userInterface.messageBox(f'File not found for URN: {urn}!')
else:
# Abandon for unhandled errors, displaying the error message.
app.userInterface.messageBox(f'Failed:{str(e)}\n{traceback.format_exc()}')
return doc