Post Toolpaths API Sample

Description

Demonstrates posting toolpaths in the active document using an NC program: import a post configuration from the post library, choose which setups or operations to include, set NC program and post parameters, and run NCProgram.postProcess.

Before running: Run from Scripts and Add-Ins with a design that contains at least one CAM setup with toolpaths. The script prompts for what to post (all setups, first setup, or first operation) and optionally opens the NC file when complete. It may offer to switch to the MANUFACTURE workspace.

Code Samples

/**
 * Post Toolpaths API Sample
 * Demonstrates posting toolpaths in the active document.
 */

import { adsk } from '@adsk/fusion';

// Params
// {
//     "useCurrentDocument": false,
//     "saveAsNewDocument": false,
//     "hubId": "wip1fqaautodesk4298",
//     "fileURN": "urn:adsk.wipqa:dm.lineage:eSYoD7PzTj66Qw9qLPS3cg",
//     "useParentFolder": true,
//     "outputFolder": "urn:adsk.wipqa:fs.folder:co.mo6KmkbWRAiBs3TZZPrvAw",
//     "targetProject": "20250120147786973",
//     "ncProgramFilename": "SampleNCProgram",
//     "postDescription": "Generic 2D"
// }

function criticalError(message:string){
  throw Error(message);}



function run(): void {

  // Read the parameters passed with the script
  const scriptParameters = JSON.parse(adsk.parameters);
  if (!scriptParameters) throw Error("Invalid parameters provided.");

  // Get the Fusion API's application object
  const app = adsk.core.Application.get();
  if (!app) throw Error("No adsk.core.Application.");

  const ui = app.userInterface

  // Log some information
  adsk.log(new Date().toString());
  adsk.log("UserName: " + app.userName);
  adsk.log("User: " + app.currentUser.displayName);
  adsk.log("UserID: " + app.userId);
  adsk.log("Version: " + app.version);

  // Create a new empty document
  //const doc: adsk.core.Document = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType);
  const doc = getDocument(
    app,
    scriptParameters.useCurrentDocument,
    scriptParameters.hubId,
    scriptParameters.fileURN,
  );
  if (!doc) throw Error("Invalid document.");

  const operationErrorMsg = 'This script requires the active document to contain at least one CAM operation.'
  const products: adsk.core.Products = doc.products;
  let product: adsk.core.Product;

  try {
        product = products.itemByProductType('CAMProductType')
        }
  catch (e) {
        criticalError(operationErrorMsg)}

  // Check if the document has a CAMProductType, abandon cleanly if not.

  const cam = products.itemByProductType("CAMProductType") as adsk.cam.CAM;
  if (!cam) throw Error("File has no CAM Product.");

  // Initialize the CAM manager to access the library manager and the post processor library.
  const camManager = adsk.cam.CAMManager.get()
  const libraryManager: adsk.cam.CAMLibraryManager = camManager.libraryManager
  const postLibrary: adsk.cam.PostLibrary = libraryManager.postLibrary

  // Create a query to filter post processors from the Fusion library by the vendor "Autodesk".
  const 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.
  let firstSetupOperationType: adsk.cam.OperationTypes
  try{
      firstSetupOperationType = cam.setups.item(0).operationType}
  catch (e){
      criticalError(operationErrorMsg)}

  if (firstSetupOperationType === adsk.cam.OperationTypes.MillingOperation){
            postQuery.capability = adsk.cam.PostCapabilities.Milling}
  else if  (firstSetupOperationType === adsk.cam.OperationTypes.TurningOperation){
            postQuery.capability = adsk.cam.PostCapabilities.Turning}
  else if  (firstSetupOperationType === adsk.cam.OperationTypes.JetOperation){
            postQuery.capability = adsk.cam.PostCapabilities.Jet}

  const postConfigs: adsk.cam.PostConfiguration[] = postQuery.execute()

  // Check for post configs being available.
  if (postConfigs.length === 0){
      criticalError('No post-configurations found.')}

  // Find the post required in the post library and import it to local library.
  const postRequired = scriptParameters.postDescription
  let postDescription: string | null = null
  let ncExtension: string | null = null
  let postConfig: adsk.cam.PostConfiguration | null = null
  let counter = 0
  for (let config of postConfigs) {

    if (config.description === postRequired) {
      postDescription = config.description
      ncExtension = config.extension
      postConfig = config
      break;
      }
  }
  if (!postConfig)
  {
    criticalError(`Unable to find postprocessor ${postRequired}.`)
  }

// Prompt the user with an option to view the resulting NC file.

// Create ncInput object.
const ncInput: adsk.cam.NCProgramInput = cam.ncPrograms.createInput()
// Set the value of scenario to:
// 1 - post all
// 2 - post the first setup,
// 3 - post only the first operation of the first setup.
const scenario = 1

// Adjust NC program parameters.
ncInput.displayName = 'Sample NCProgram'
// Embelish the nc filename to indicate chosen scenario and programming language.
const programName = `${scriptParameters.ncProgramFilename}${scenario}`;
const programfilename: adsk.cam.CAMParameter = ncInput.parameters.itemByName('nc_program_filename');
(<adsk.cam.StringParameterValue>(programfilename?.value)).value = programName

// Provide a comment at the top of the file.
const comment: adsk.cam.CAMParameter = ncInput.parameters.itemByName('nc_program_comment');
(<adsk.cam.StringParameterValue>(comment?.value)).value = "This is the Post Toolpaths API Sample NC Program";

// Setting the output folder parameters
(<adsk.cam.BooleanParameterValue>(ncInput.parameters.itemByName("nc_program_postToFusionTeam")?.value)).value = true;

// Setting Fusion Teams Output Folder Parameter
(<adsk.cam.StringParameterValue>(ncInput.parameters.itemByName("nc_program_internal_fusion_team_output_folder")?.value)).value = getFolderId(
    app,
    scriptParameters.useParentFolder,
    scriptParameters.outputFolder,
  );

// Setting the Fusion Teams Output folder Project ID Parameter
(<adsk.cam.StringParameterValue>(ncInput.parameters.itemByName("nc_program_fusion_team_target_project_id")?.value)).value = scriptParameters.targetProject;


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
const opsBaseList: adsk.cam.OperationBase[] = []
const setups: adsk.cam.Setups = cam.setups
for (let i: number; i < cam.setups.count; i++){
    const setup = cam.setups.item(i)
    opsBaseList.push(setup)
    ncInput.operations = opsBaseList} // Include all setups}
}
else if (scenario === 2){
  ncInput.operations = [cam.setups.item(0)]  // Only include the first setup
}

else if (scenario === 3){
const firstSetup: adsk.cam.Setup = cam.setups.item(0)
const firstOperation = firstSetup.allOperations.item(0) as adsk.cam.Operation
if (firstOperation.hasToolpath){
  ncInput.operations = [firstOperation]}  // Only include the first operation of the first setup
else {
    criticalError('Operation has no toolpath to post');}
}

 // Create a new NC program using the current NC Program input including specified parameters and operations
const 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.
const postParams: adsk.cam.CAMParameters = newProgram.postParameters
// Parameter builtin_tolerance is of Type FloatParameterValue.
const builtInTolerance: adsk.cam.CAMParameter = postParams.itemByName('builtin_tolerance');
(<adsk.cam.FloatParameterValue>(builtInTolerance.value)).value = 0;
// Parameter showSequenceNumbers is of type ChoiceParameterValue so "false" is a string not a boolean.
const showSequenceNumbers: adsk.cam.CAMParameter = postParams.itemByName('showSequenceNumber');
// Not all posts support SequenceNumbers
// if (showSequenceNumbers){
//     (<adsk.cam.ChoiceParameterValue>(showSequenceNumbers.value)).value = 'false';
//     }
// else{
//     criticalError(`Parameter showSequenceNumbers cannot be modified for postprocessor: ${postRequired} - skipping`)
//     }
// Update to apply changes.
newProgram.updatePostParameters(postParams)
// Finally post the process.
const postOptions = adsk.cam.NCProgramPostProcessOptions.create()
newProgram.postProcess(postOptions)

// Waiting for the Posting to complete
  while (app.hasActiveJobs) {
    wait(4000);
  }
  wait(5000);

}

function getFolderId(
  app: adsk.core.Application,
  useParentFolder: boolean,
  outputFolder: string,
): string {
  if (useParentFolder) {
    return app.activeDocument.dataFile.parentFolder.id;
  }
  return outputFolder;
}


function getDocument(
  app: adsk.core.Application,
  useCurrentDocument: boolean,
  hubId: string,
  fileURN: string,
): adsk.core.Document {
  if (useCurrentDocument === true) {
    adsk.log(`Using currently open document: ${app.activeDocument.name}.`);
    return app.activeDocument;
  }

  if (hubId) {
    // Possible hubId formats: base64 encoded string, or business:<id>,
    // or personal:<id> (deprecated)
    const hub =
      app.data.dataHubs.itemById(hubId) ||
      app.data.dataHubs.itemById(`a.${adsk.btoa(`business:${hubId}`, true)}`) ||
      app.data.dataHubs.itemById(`a.${adsk.btoa(`personal:${hubId}`, true)}`);
    if (!hub) throw Error(`Hub with id ${hubId} not found.`);
    adsk.log(`Setting hub: ${hub.name}.`);
    app.data.activeHub = hub;
  }

  const file = app.data.findFileById(fileURN);
  if (!file) throw Error(`File not found ${fileURN}.`);
  adsk.log(`Opening ${file.name}`);
  const document = app.documents.open(file, true);
  if (!document) throw Error(`Cannot open file ${file.name}.`);
  return document;
}

function wait(ms: number) {
  const start = new Date().getTime();
  while (new Date().getTime() - start < ms) adsk.doEvents();
}


run();
/////////////////////////////////////////////////////////////////////////////////////////////////
// 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()))