/**
  Copyright (C) 2012-2024 by Autodesk, Inc.
  All rights reserved.

  CIMCO scanning post processor configuration.

  $Revision: 44155 f6d6b16b1938481fa6b4d9e68439883336a975fd $
  $Date: 2024-12-09 09:16:26 $

  FORKID {C8177EFA-F76F-4667-AEF9-726A14023E07}
*/

description = "CIMCO Scanning";
vendor = "CIMCO";
vendorUrl = "https://www.cimco.com/";
legal = "Copyright (C) 2012-2024 by Autodesk, Inc.";
certificationLevel = 2;
minimumRevision = 45948;
capabilities = CAPABILITY_INTERMEDIATE | CAPABILITY_CASCADING;
longDescription = "CIMCO scanning post processor for Autodesk Fusion. This post creates the required setup files with tools, models, and stock information for CIMCO scanning functionality." + EOL
    + "If a fixture and fixture coordinate system are defined in the setup, the part will be placed relative to the fixture coordinate system. Otherwise, the part will be placed relative to the WCS origin.";

var valueFormat = createFormat({decimals:(unit == MM ? 3 : 4)});
var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4)});

this.exportPart = true;
this.exportFixture = true;
this.exportStock = true;

var settings = {
  comments: {
    permittedCommentChars: "abcdefghijklmnopqrstuvwxyz0123456789_-=+", // letters are not case sensitive, use option 'outputFormat' below. Set to 'undefined' to allow any character
    prefix               : "\"", // specifies the prefix for the comment
    suffix               : "\"", // specifies the suffix for the comment
    outputFormat         : "upperCase", // can be set to "upperCase", "lowerCase" and "ignoreCase". Set to "ignoreCase" to write comments without upper/lower case formatting
    maximumLineLength    : 80 // the maximum number of characters allowed in a line, set to 0 to disable comment output
  }
};

function onSection() {
  if (currentSection.getType() != TYPE_MILLING) { // Only support for milling tools
    error(localize("Unsupported operation type. CIMCO scanning feature is supported only for milling operations."));
  }
  skipRemainingSection();
}

var file;
var destPath = FileSystem.getFolderPath(getCascadingPath());
function onClose() {
  var filePath = FileSystem.replaceExtension(getCascadingPath(), "setup");
  file = new TextFile(filePath, true, "utf-8");
  createVerificationJob();
  createToolDatabaseFile();
  file.close();

  writeln(localize("This post is a cascading post processor scanning output file will be located in : " + filePath));
}

function getSectionParameterForTool(tool, id, defaultValue) {
  var numberOfSections = getNumberOfSections();
  for (var i = 0; i < numberOfSections; ++i) {
    var section = getSection(i);
    if (section.getTool().number == tool.number) {
      return section.getParameter(id, defaultValue);
    }
  }
  return defaultValue;
}

function getToolName(tool) {
  switch (tool.type) {
  case TOOL_MILLING_END_FLAT:
  case TOOL_MILLING_END_BALL:
  case TOOL_MILLING_END_BULLNOSE:
    return "END MILL";
  case TOOL_MILLING_FACE:
    return "FACE MILL";
  case TOOL_MILLING_SLOT:
    return "SLOT MILL";
  case TOOL_MILLING_CHAMFER:
    return "CHAMFER MILL";
  case TOOL_MILLING_RADIUS:
    return "RADIUS MILL";
  case TOOL_MILLING_DOVETAIL:
    return "DOVETAIL MILL";
  case TOOL_MILLING_TAPERED:
    return "TAPERED MILL";
  case TOOL_MILLING_LOLLIPOP:
    return "LOLLIPOP MILL";
  case TOOL_MILLING_THREAD:
    return "THREAD MILL";
  case TOOL_DRILL:
    return "DRILL";
  case TOOL_DRILL_CENTER:
    return "CENTER DRILL";
  case TOOL_DRILL_SPOT:
    return "SPOT DRILL";
  case TOOL_REAMER:
    return "REAMER";
  case TOOL_BORING_BAR:
    return "COUNTER BORE";
  case TOOL_COUNTER_BORE:
    return "COUNTER BORE";
  case TOOL_COUNTER_SINK:
    return "COUNTER SINK";
  case TOOL_TAP_RIGHT_HAND:
    return "RH TAP";
  case TOOL_TAP_LEFT_HAND:
    return "LH TAP";
  case TOOL_PROBE:
    return "PROBE";
  case TOOL_MILLING_CIRCLE_SEGMENT_LENS:
    return "LENS FORM";
  case TOOL_MILLING_CIRCLE_SEGMENT_OVAL:
    return "OVAL FORM";
  case TOOL_MILLING_CIRCLE_SEGMENT_BARREL:
    return "BARREL FORM";
  default:
    return "default";
  }
}

validate(settings.comments, "Setting 'comments' is required but not defined.");
function formatComment(text) {
  var prefix = settings.comments.prefix;
  var suffix = settings.comments.suffix;
  var _permittedCommentChars = settings.comments.permittedCommentChars == undefined ? "" : settings.comments.permittedCommentChars;
  switch (settings.comments.outputFormat) {
  case "upperCase":
    text = text.toUpperCase();
    _permittedCommentChars = _permittedCommentChars.toUpperCase();
    break;
  case "lowerCase":
    text = text.toLowerCase();
    _permittedCommentChars = _permittedCommentChars.toLowerCase();
    break;
  case "ignoreCase":
    _permittedCommentChars = _permittedCommentChars.toUpperCase() + _permittedCommentChars.toLowerCase();
    break;
  default:
    error(localize("Unsupported option specified for setting 'comments.outputFormat'."));
  }
  if (_permittedCommentChars != "") {
    text = filterText(String(text), _permittedCommentChars);
  }
  text = String(text).substring(0, settings.comments.maximumLineLength - prefix.length - suffix.length);
  return text != "" ?  prefix + text + suffix : "";
}

function hasHolder(tool) {
  return tool.holder && tool.holder.hasSections();
}

function writeToolHolder(tool) {
  var holder = tool.holder;
  if (hasHolder(tool)) {
    var holderId = "H" + tool.number;
    var US = unit == MM ? "UM" : "UI";
    writeBlock("HOLDER BEGIN", holderId, formatComment(tool.holderDescription), US);
    var n = holder.getNumberOfSections();
    for (var i = n - 1; i > 0; i--) {
      var segmentLength = valueFormat.format(holder.getLength(i));
      var upperDia = valueFormat.format(holder.getDiameter(i));
      var lowerDia = valueFormat.format(holder.getDiameter(i - 1));
      if (segmentLength != 0) {
        writeBlock(upperDia + ",", lowerDia + ",", segmentLength);
      }
    }
    writeBlock("HOLDER END");
  }
}

function createToolDatabaseFile() {
  var tools = getToolList();
  for (var i = 0; i < tools.length; ++i) {
    var tool = tools[i].tool;
    var toolType = tool.getType();

    var D = "D=" + valueFormat.format(tool.diameter);
    var FL = "FL=" + valueFormat.format(tool.fluteLength);
    var CD = "CD=0"; // tool edge chamfer
    var CR = "CR=" + valueFormat.format(tool.cornerRadius);
    var BL = "BL=" + valueFormat.format(tool.bodyLength);
    var AD = "AD=" + valueFormat.format(tool.shaftDiameter);
    var SD = "SD=" + valueFormat.format(tool.diameter); // shoulder diameter
    var SL = "SL=" + valueFormat.format(tool.shoulderLength);
    var US = unit == MM ? "US=UM" : "US=UI";
    var tipAngle = getSectionParameterForTool(tool, "operation:tool_tipAngle", 118);
    var toolId = "TOOL " + tool.number;
    var toolName = "\"" + getToolName(tool) + "\"";
    var holderId = hasHolder(tool) ? "HOLDER=H" + tool.number : "";

    switch (toolType) {
    case TOOL_MILLING_END_FLAT:
    case TOOL_MILLING_END_BALL:
    case TOOL_MILLING_END_BULLNOSE:
      var EMCT = toolType == TOOL_MILLING_END_FLAT ? "FEM" : toolType == TOOL_MILLING_END_BALL ? "BEM" : "BNEM";
      writeBlock(toolId, toolName, holderId, BL, CD, CR, "EMCT=" + EMCT, D, FL, US,
        SD, AD, SL);
      break;

    case TOOL_MILLING_FACE:
      var TD = "TD=" + valueFormat.format(tool.diameter - (2 * tool.cornerRadius));
      writeBlock(toolId, toolName, holderId, BL, CR, D, FL,
        US, AD, SL, TD,  "TA=" + valueFormat.format(tool.taperAngle));
      break;

    case TOOL_MILLING_TAPERED:
    case TOOL_MILLING_DOVETAIL:
      writeBlock(toolId, toolName, holderId, BL, CR, D, FL,
        US, AD, SL, "A=" + valueFormat.format(tool.taperAngle));
      break;

    case TOOL_MILLING_RADIUS:
      writeBlock(toolId, toolName, holderId, BL, CR, "TD=" + D, FL, US, AD, SL);
      break;

    case TOOL_MILLING_CHAMFER:
      writeBlock(toolId, toolName, holderId, BL, D, FL, "CHTYPE=CHTYPEDA",
        US, AD, SL, "A=" + valueFormat.format(tool.taperAngle));
      break;

    case TOOL_MILLING_LOLLIPOP:
      writeBlock(toolId, toolName, holderId, BL, FL, D, US, AD, SL, SD, "TL=0");
      break;

    case TOOL_MILLING_SLOT:
      writeBlock(toolId, toolName, holderId, BL, CD, CR, D,
        FL, US, SD, AD, SL, "TL=0 TCD=0 CRT=" + valueFormat.format(tool.cornerRadius));
      break;

    case TOOL_MILLING_THREAD:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, SD, AD, SL,
        "NTT=" + getSectionParameterForTool(tool, "operation:tool_numberOfTeeth", 1),
        "TP=" + valueFormat.format(tool.threadPitch));
      break;

    case TOOL_DRILL:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, SD, AD, SL, "TL=0", "TA=" + tipAngle);
      break;

    case TOOL_DRILL_CENTER:
      var taperAngle = getSectionParameterForTool(tool, "operation:tool_taperAngle", 90) / 2;
      writeBlock(toolId, toolName, holderId, BL,
        "BD=" + valueFormat.format(tool.shaftDiameter - 0.1), "BA=" + taperAngle,
        "D=" + valueFormat.format(tool.tipDiameter), FL, US, AD,
        "AT=" + tipAngle, "A=" + taperAngle,
        "TIPL=" + getSectionParameterForTool(tool, "operation:tool_tipLength", 0));
      break;

    case TOOL_DRILL_SPOT:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, AD, SL, SD, "TL=0",
        "AT=" + tipAngle,
        "TD=" + valueFormat.format(tool.tipDiameter));
      break;

    case TOOL_REAMER:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, AD, "CD=0");
      break;

    case TOOL_BORING_BAR:
    case TOOL_COUNTER_BORE:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, AD, SL);
      break;

    case TOOL_COUNTER_SINK:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, AD, SL,
        "AT=" + getSectionParameterForTool(tool, "operation:tool_tipAngle", 90));
      break;

    case TOOL_TAP_RIGHT_HAND:
    case TOOL_TAP_LEFT_HAND:
      writeBlock(toolId, toolName, holderId, BL, D, FL, US, AD, SL, SD, "TL=0",
        "TP=" +  valueFormat.format(tool.threadPitch), "AT=180");
      break;

    case TOOL_PROBE:
      writeBlock(toolId, toolName, holderId, BL, D, US, AD);
      break;

    default:
      break;
    }
    writeToolHolder(tool);
  }
}

function writeBlock() {
  var text = formatWords(arguments);
  if (!text) {
    return;
  }
  file.writeln(text);
}

var destStockPath = "";
var destPartPath = "";
var destFixturePath = "";
function createVerificationJob() {
  var US = unit == MM ? "UM" : "UI";
  var stockPath = getGlobalParameter("autodeskcam:stock-path", "");
  var partPath = getGlobalParameter("autodeskcam:part-path", "");
  var fixturePath = getGlobalParameter("autodeskcam:fixture-path", "");

  var fcsOrigin = new Vector(0, 0, 0);
  if (fixturePath && getSection(0).getFCSOrigin().isNonZero()) {
    fcsOrigin.x = getSection(0).getFCSOrigin().x * -1;
    fcsOrigin.y = getSection(0).getFCSOrigin().y * -1;
    fcsOrigin.z = Math.abs(getSection(0).getFCSOrigin().z);
  }
  writeBlock("WCS ID" + getSection(0).workOffset, "X" + xyzFormat.format(fcsOrigin.x * (unit == MM ? 1 : 25.4)), "Y" + xyzFormat.format(fcsOrigin.y * (unit == MM ? 1 : 25.4)), "Z" + xyzFormat.format(fcsOrigin.z * (unit == MM ? 1 : 25.4)), "A0 B0 C0"); //wcs offset always expects units in mm.

  if (!FileSystem.isFolder(destPath)) {
    error(subst(localize("NC job folder '%1' does not exist."), destPath));
  }

  if (!programName) {
    error(localize("Program name is not specified."));
  }

  if (FileSystem.isFile(stockPath)) {
    destPartPath = FileSystem.getCombinedPath(destPath, programName + "_STOCK.stl");
    FileSystem.copyFile(stockPath, destPartPath);
    writeBlock("STOCK STL", "PATH=\"" + destPartPath + "\"", "X" + xyzFormat.format(fcsOrigin.x), "Y" + xyzFormat.format(fcsOrigin.y), "Z" + xyzFormat.format(fcsOrigin.z), "A0 B0 C0", US);
  }

  if (FileSystem.isFile(partPath)) {
    destPartPath = FileSystem.getCombinedPath(destPath, programName + "_PART.stl");
    FileSystem.copyFile(partPath, destPartPath);
    writeBlock("WORKPIECE ID1", "\"" + destPartPath + "\"", "X" + xyzFormat.format(fcsOrigin.x), "Y" + xyzFormat.format(fcsOrigin.y), "Z" + xyzFormat.format(fcsOrigin.z), "A0 B0 C0", US, "RGB=40,140,140");
  }

  if (FileSystem.isFile(fixturePath)) {
    destFixturePath = FileSystem.getCombinedPath(destPath, programName + "_FIXTURE.stl");
    FileSystem.copyFile(fixturePath, destFixturePath);
    writeBlock("FIXTURE ID1", "\"" + destFixturePath + "\"",  "X" + xyzFormat.format(fcsOrigin.x), "Y" + xyzFormat.format(fcsOrigin.y), "Z" + xyzFormat.format(fcsOrigin.z), "A0 B0 C0", US, "RGB=40,140,140");
  }
}