/////////////////////////////////////////////////////////////////////////////////////////////////
// For this sample script to run, the active Fusion document needs at least one CAM operation. //
/////////////////////////////////////////////////////////////////////////////////////////////////
#include <Core/CoreAll.h>
#include <CAM/CAMAll.h>
#ifdef _WINDOWS
#include <shlwapi.h>
#else
#include <stdlib.h>
#endif
using namespace adsk::core;
using namespace adsk::cam;
// Define a base class pointer for common usage.
using CAMObjectPtr = Ptr<OperationBase>;
// Function to ensure pathnames use forward slashes as required by the NC Program Output Folder.
void replaceBackslashWithSlash(std::string& str)
{
for (char& ch : str)
if (ch == '\\')
ch = '/';
}
// Customised messageBox to handle critical errors in one line
void criticalError(Ptr<UserInterface> ui, const std::string& message)
{
ui->messageBox(
message, "Critical Error", MessageBoxButtonTypes::OKButtonType, MessageBoxIconTypes::CriticalIconType);
}
extern "C" XI_EXPORT bool run(const char* context)
{
Ptr<Application> app = Application::get();
if (!app)
return false;
Ptr<UserInterface> ui = app->userInterface();
if (!ui)
return false;
Ptr<Document> doc = app->activeDocument();
if (!doc)
return false;
Ptr<Products> products = doc->products();
if (!products)
return false;
// Check if the document has a CAMProductType, abandon cleanly if not.
std::string operationErrorMsg = "This script requires the active document to contain at least one CAM operation.";
Ptr<CAM> camProduct = products->itemByProductType("CAMProductType");
if (!camProduct)
{
criticalError(ui, operationErrorMsg);
return false;
}
// Initialize the CAM manager to access the library manager and the post processor library.
Ptr<CAMManager> camManager = CAMManager::get();
Ptr<CAMLibraryManager> libraryManager = camManager->libraryManager();
Ptr<PostLibrary> postLibrary = libraryManager->postLibrary();
// Create a query to filter post processors from the Fusion library by the vendor "Autodesk".
Ptr<PostConfigurationQuery> postQuery = postLibrary->createQuery(LibraryLocations::Fusion360LibraryLocation);
postQuery->vendor("Autodesk");
// Set the post configuration to use based on Operation Type of the first Setup.
Ptr<Setup> setup;
Ptr<Setups> setups = camProduct->setups();
if (setups)
{
setup = setups->item(0);
if (!setup)
{
criticalError(ui, operationErrorMsg);
return false;
}
}
OperationTypes opType = setup->operationType();
switch (opType)
{
case adsk::cam::MillingOperation:
postQuery->capability(adsk::cam::PostCapabilities::Milling);
break;
case adsk::cam::TurningOperation:
postQuery->capability(adsk::cam::PostCapabilities::Turning);
break;
case adsk::cam::JetOperation:
postQuery->capability(adsk::cam::PostCapabilities::Jet);
break;
default:
postQuery->capability(adsk::cam::PostCapabilities::Milling);
break;
}
std::vector<Ptr<PostConfiguration>> postConfigs = postQuery->execute();
Ptr<URL> importedURL;
std::string ncExtension;
std::string postRequired = "RS-274D";
std::string postDescription = "";
// Find the required post in the post library and import it to local library.
for (Ptr<PostConfiguration>& config : postConfigs)
{
if (config->description() == postRequired)
{
postDescription = postRequired;
ncExtension = config->extension();
Ptr<URL> configUrl = URL::create("user://");
importedURL = postLibrary->importPostConfiguration(config, configUrl, "NCProgramSamplePost.cps");
break;
}
}
// Check if the required post was found, abandon cleanly if not.
if (postDescription.empty())
{
criticalError(ui, "Unable to find postprocessor " + postRequired + ".");
return false;
}
// Get the imported local post config.
Ptr<PostConfiguration> postConfig = postLibrary->postConfigurationAtURL(importedURL);
// Prompt the user with an option to view the resulting NC file.
DialogResults dlgResults = ui->messageBox(
"View nc file when done?",
"Post-processing",
MessageBoxButtonTypes::YesNoButtonType,
MessageBoxIconTypes::QuestionIconType);
bool viewResult = (dlgResults == DialogResults::DialogYes);
// Create NCProgramInput object
Ptr<NCProgramInput> ncInput = camProduct->ncPrograms()->createInput();
// Set the value of scenario to 1, 2 or 3 to post all, post the first setup,
// or post only the first operation of the first setup.
int scenario = 1;
std::string message = "Yes \t- All toolpaths will be posted.\n\n"
"No \t- Toolpaths in the first setup will be posted.\n\n"
"Cancel\t- The first toolpath in the first setup will be posted.\n";
// Spaces appended to the title make the messageBox wider, preventing linw wrapping.
DialogResults mbChoice = ui->messageBox(
message,
"Select what should be post-processed ",
MessageBoxButtonTypes::YesNoCancelButtonType,
MessageBoxIconTypes::QuestionIconType);
if (mbChoice == DialogResults::DialogYes)
{
scenario = 1;
}
else if (mbChoice == DialogResults::DialogNo)
{
scenario = 2;
}
else
{
scenario = 3;
}
// Adjust NC program parameters.
ncInput->displayName("Sample NCProgram");
Ptr<CAMParameters> ncParameters = ncInput->parameters();
Ptr<StringParameterValue> nc_program_filename = ncParameters->itemByName("nc_program_filename")->value();
// Embelish the nc filename to indicate chosen scenario and programming language.
std::string programName = "NCProgramSampleCPP" + std::to_string(scenario);
nc_program_filename->value(programName);
// Provide a comment at the top of the file.
Ptr<StringParameterValue> nc_program_comment = ncParameters->itemByName("nc_program_comment")->value();
std::string comment = "This is the Post Toolpaths API Sample NC Program";
nc_program_comment->value(comment);
// Open nc file in editor if earlier requested.
Ptr<BooleanParameterValue> nc_program_openInEditor = ncParameters->itemByName("nc_program_openInEditor")->value();
nc_program_openInEditor->value(viewResult);
// Set temp directory as output directory.
std::string outputFolder = camProduct->temporaryFolder();
// Variable outputFolder may contain backslashes but we need forward slashes.
replaceBackslashWithSlash(outputFolder);
Ptr<StringParameterValue> nc_program_output_folder = ncParameters->itemByName("nc_program_output_folder")->value();
nc_program_output_folder->value(outputFolder);
// We have already declared a setups Ptr for all 3 scenarios.
// Scenarios 2 and 3 use firstSetup.
Ptr<Setup> firstSetup = setups->item(0);
switch (scenario)
{
case 1:
{
// ncInput->operations() expects a list of type 'OperationBase'.
// For example: Operation, CAMFolder, CAMPattern, Setup.
// It will not accept CAMFolders, CAMPatterns or Setups (all plural)
// as they are derived from core.Base. We make our own list instead.
// Create a vector for the content of core.Base objects.
std::vector<CAMObjectPtr> camObjects;
for (size_t i = 0; i < setups->count(); ++i)
{
// Add the operation to the operations list.
camObjects.push_back(setups->item(i));
}
ncInput->operations({camObjects}); // Include all setups
break;
}
case 2:
{
ncInput->operations({firstSetup}); // Only include the first setup
break;
}
case 3:
{
Ptr<Operation> firstOperation = firstSetup->allOperations()->item(0);
if (firstOperation->hasToolpath())
{
ncInput->operations({firstOperation}); // First operation of first setup
}
else
{
criticalError(ui, "Operation has no toolpath to post");
return false;
}
break;
}
}
// Create a new NC program using NC Program input and specified parameters and operations.
Ptr<NCProgram> newProgram = camProduct->ncPrograms()->add(ncInput);
// Configure the post processor
newProgram->postConfiguration(postConfig);
// Example of changing a post parameter. Parameters need to be declared as their correct type.
Ptr<CAMParameters> postParams = newProgram->postParameters();
// Parameter builtin_tolerance is of Type FloatParameterValue.
Ptr<FloatParameterValue> builtInTolerance = postParams->itemByName("builtin_tolerance")->value();
// Demonstrating how to check current parameter value and modify it.
std::string previous = "Previous builtin_tolerance: " + std::to_string(builtInTolerance->value());
builtInTolerance->value(0.004);
app->log(previous + "\tNew value: " + std::to_string(builtInTolerance->value()));
// The next assignment returns null if you use a post-processor that does not support
// block numbering - for example "XYZ" or "Generic 2D", however you will not need it in that case.
Ptr<CAMParameter> showSequenceNumbersParam = postParams->itemByName("showSequenceNumbers");
if (showSequenceNumbersParam)
{
// Parameter showSequenceNumbers is of type ChoiceParameterValue so "false" is a string not a boolean.
Ptr<ChoiceParameterValue> showSequenceNumbers = postParams->itemByName("showSequenceNumbers")->value();
// Demonstrating how to check current parameter value and modify it.
previous = "Previous showSequenceNumbers: " + showSequenceNumbers->value();
showSequenceNumbers->value("false");
app->log(previous + "\tNew value: " + showSequenceNumbers->value());
}
// Update to apply changes.
newProgram->updatePostParameters(postParams);
// Finally post the process.
Ptr<NCProgramPostProcessOptions> postOptions = adsk::cam::NCProgramPostProcessOptions::create();
newProgram->postProcess(postOptions);
// Activate CAM workspace if it is not the active one.
if (ui->activeWorkspace()->name() != "Manufacture")
{
DialogResults result = ui->messageBox(
"Activate the CAM Workspace?",
"CAM Workspace Activate",
MessageBoxButtonTypes::YesNoButtonType,
MessageBoxIconTypes::QuestionIconType);
if (result == DialogResults::DialogYes)
{
Ptr<Workspace> camWorkspace = ui->workspaces()->itemById("CAMEnvironment");
camWorkspace->activate();
}
}
// Indicate completion.
ui->messageBox(
"Post processing is complete. See file:\n" + outputFolder + "/" + programName + ncExtension,
"Post Toolpath API Sample \t\t\t\t\t"); // Prevent line breaks
return true;
}
# For this sample script to run, the active Fusion document needs at least one CAM operation.
import adsk.core, adsk.fusion, adsk.cam, traceback
app = adsk.core.Application.get()
ui = app.userInterface
def criticalError(message:str):
''' Customised messageBox to handle critical errors in one line '''
ui.messageBox( message,
'Critical Error',
adsk.core.MessageBoxButtonTypes.OKButtonType,
adsk.core.MessageBoxIconTypes.CriticalIconType)
def run(context):
try:
doc: adsk.core.Document = app.activeDocument
products: adsk.core.Products = doc.products
try:
product: adsk.core.Product = products.itemByProductType('CAMProductType')
except Exception:
product = None
# Check if the document has a CAMProductType, abandon cleanly if not.
operationErrorMsg = 'This script requires the active document to contain at least one CAM operation.'
if product == None:
criticalError(operationErrorMsg)
return
cam = adsk.cam.CAM.cast(product)
# Initialize the CAM manager to access the library manager and the post processor library.
camManager = adsk.cam.CAMManager.get()
libraryManager: adsk.cam.CAMLibraryManager = camManager.libraryManager
postLibrary: adsk.cam.PostLibrary = libraryManager.postLibrary
# Create a query to filter post processors from the Fusion library by the vendor "Autodesk".
postQuery: adsk.cam.PostConfigurationQuery = \
postLibrary.createQuery(adsk.cam.LibraryLocations.Fusion360LibraryLocation)
postQuery.vendor = "Autodesk"
# Set the post configuration to use based on Operation Type of the first Setup.
try:
firstSetupOperationType: adsk.cam.OperationTypes = cam.setups.item(0).operationType
except:
criticalError(operationErrorMsg)
return
if firstSetupOperationType == adsk.cam.OperationTypes.MillingOperation:
postQuery.capability = adsk.cam.PostCapabilities.Milling
elif firstSetupOperationType == adsk.cam.OperationTypes.TurningOperation:
postQuery.capability = adsk.cam.PostCapabilities.Turning
elif firstSetupOperationType == adsk.cam.OperationTypes.JetOperation:
postQuery.capability = adsk.cam.PostCapabilities.Jet
postConfigs: list[adsk.cam.PostConfiguration] = postQuery.execute()
# Check for post configs being available.
if len(postConfigs) == 0:
criticalError('No post-configurations found.')
return
# Find the post required in the post library and import it to local library.
postRequired = "RS-274D"
postDescription = None
for config in postConfigs:
if config.description == postRequired:
postDescription = postRequired
ncExtension = config.extension
url = adsk.core.URL.create("user://")
importedURL = postLibrary.importPostConfiguration(config, url, "NCProgramSamplePost.cps")
break
# Check if the required post was found, abandon cleanly if not.
if not postDescription:
criticalError(f'Unable to find postprocessor {postRequired}.')
return
# Get the imported local post config.
postConfig: adsk.cam.PostConfiguration = postLibrary.postConfigurationAtURL(importedURL)
# Prompt the user with an option to view the resulting NC file.
dlgResults: adsk.core.DialogResults = ui.messageBox(
'View results when post is complete?', 'Post-processing',
adsk.core.MessageBoxButtonTypes.YesNoButtonType,
adsk.core.MessageBoxIconTypes.QuestionIconType)
viewResult: bool = (dlgResults == adsk.core.DialogResults.DialogYes)
# Create ncInput object.
ncInput: adsk.cam.NCProgramInput = cam.ncPrograms.createInput()
# Set the value of scenario to 1, 2 or 3 to post all, post the first setup,
# or post only the first operation of the first setup.
scenario = 1
# Spaces appended to the title make the messageBox wider, preventing linw wrapping.
mbChoice: adsk.core.DialogResults = ui.messageBox(
"Yes \t- All toolpaths will be posted.\n\n" \
"No \t- Toolpaths in the first setup will be posted.\n\n" \
"Cancel\t- The first toolpath in the first setup will be posted.\n",
"Select what should be post-processed ",
adsk.core.MessageBoxButtonTypes.YesNoCancelButtonType,
adsk.core.MessageBoxIconTypes.QuestionIconType)
if (mbChoice == adsk.core.DialogResults.DialogYes):
scenario = 1
elif (mbChoice == adsk.core.DialogResults.DialogNo):
scenario = 2
else:
scenario = 3
# Adjust NC program parameters.
ncInput.displayName = 'Sample NCProgram'
# Embelish the nc filename to indicate chosen scenario and programming language.
programName:str = f'NCProgramSamplePy{scenario}'
programfilename: adsk.cam.CAMParameter = ncInput.parameters.itemByName('nc_program_filename')
programfilename.value.value = programName
# Provide a comment at the top of the file.
comment: adsk.cam.CAMParameter = ncInput.parameters.itemByName('nc_program_comment')
comment.value.value = "This is the Post Toolpaths API Sample NC Program"
# Open nc file in editor if earlier requested.
openInEditor: adsk.cam.CAMParameter = ncInput.parameters.itemByName('nc_program_openInEditor')
openInEditor.value.value = viewResult
# Set temp directory as output directory
# Make the path valid for Fusion by replacing \\ to / in the path
outputFolder = str(cam.temporaryFolder).replace('\\', '/')
ncInput.parameters.itemByName('nc_program_output_folder').value.value = outputFolder
if scenario == 1:
# ncInput.operations() expects a list of type 'OperationsBase': Operation, CAMFolder, CAMPattern, Setup
# It will not accept CAMFolders, CAMPatterns or Setups (all plural) as they are derived from core.Base
opsBaseList: list[adsk.cam.OperationBase] = []
setups: adsk.cam.Setups = cam.setups
for setup in setups:
opsBaseList.append(setup)
ncInput.operations = opsBaseList # Include all setups
elif scenario == 2:
ncInput.operations = [cam.setups.item(0)] # Only include the first setup
elif scenario == 3:
firstSetup: adsk.cam.Setup = cam.setups.item(0)
firstOperation: adsk.core.Base = firstSetup.allOperations.item(0)
if firstOperation.hasToolpath:
ncInput.operations = [firstOperation] # Only include the first operation of the first setup
else:
ui.messageBox('Operation has no toolpath to post')
return
# Create a new NC program using the current NC Program input including specified parameters and operations
newProgram: adsk.cam.NCProgram = cam.ncPrograms.add(ncInput)
# Configure the post processor
newProgram.postConfiguration = postConfig
# Example of changing a post parameter. Parameters need to be declared as their correct type.
postParams: adsk.cam.CAMParameters = newProgram.postParameters
# Parameter builtin_tolerance is of Type FloatParameterValue.
builtInTolerance: adsk.cam.FloatParameterValue = postParams.itemByName('builtin_tolerance')
builtInTolerance.value.value = 0.004
# Parameter showSequenceNumbers is of type ChoiceParameterValue so "false" is a string not a boolean.
showSequenceNumbers: adsk.cam.ChoiceParameterValue = postParams.itemByName('showSequenceNumbers')
# Not all posts support SequenceNumbers
if showSequenceNumbers:
showSequenceNumbers.value.value = str('false')
else:
criticalError(f'Parameter showSequenceNumbers cannot be modified for postprocessor: {postRequired} - skipping!')
# Update to apply changes.
newProgram.updatePostParameters(postParams)
# Finally post the process.
postOptions = adsk.cam.NCProgramPostProcessOptions.create()
newProgram.postProcess(postOptions)
# Prompt user with an option to switch to the CAM workspace if it's not already active
if ui.activeWorkspace.name != 'Manufacture':
activateCAMWorkspace: adsk.core.DialogResults = ui.messageBox(
f'Activate the CAM Workspace?','CAM Workspace Activate',
adsk.core.MessageBoxButtonTypes.YesNoButtonType,
adsk.core.MessageBoxIconTypes.QuestionIconType)
if activateCAMWorkspace == adsk.core.DialogResults.DialogYes:
camWorkspace: adsk.core.Workspace = ui.workspaces.itemById("CAMEnvironment")
camWorkspace.activate()
# Indicate completion.
ui.messageBox(f'See file:\n{outputFolder}/{programName}{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()))