Skip to content
kondia-b500.cps 69.9 KiB
Newer Older
ellyan's avatar
ellyan committed
/**
ellyan's avatar
ellyan committed
  Copyright (C) 2012-2023 by Autodesk, Inc.
ellyan's avatar
ellyan committed
  All rights reserved.

  NUM post processor configuration.

ellyan's avatar
ellyan committed
  $Revision: 44080 7098e6d148aa6ea195b42597d82fe2f00d0115bb $
ellyan's avatar
ellyan committed
  $Date: 2023-07-17 12:38:58 $
ellyan's avatar
ellyan committed

  FORKID {8B1FC740-38BD-4cb2-9CD5-94A55CBE87A5}
*/

description = "NUM";
vendor = "NUM";
vendorUrl = "http://www.num.com";
ellyan's avatar
ellyan committed
legal = "Copyright (C) 2012-2023 by Autodesk, Inc.";
ellyan's avatar
ellyan committed
certificationLevel = 2;
ellyan's avatar
ellyan committed
minimumRevision = 45917;
ellyan's avatar
ellyan committed

longDescription = "Generic milling post for NUM.";

extension = "nc";
programNameIsInteger = true;
setCodePage("ascii");

ellyan's avatar
ellyan committed
capabilities = CAPABILITY_MILLING | CAPABILITY_MACHINE_SIMULATION;
ellyan's avatar
ellyan committed
tolerance = spatial(0.002, MM);

minimumChordLength = spatial(0.25, MM);
minimumCircularRadius = spatial(0.01, MM);
maximumCircularRadius = spatial(1000, MM);
minimumCircularSweep = toRad(0.01);
maximumCircularSweep = toRad(5400);
allowHelicalMoves = true;
allowedCircularPlanes = undefined; // allow any circular motion

// user-defined properties
properties = {
  writeMachine: {
    title      : "Write machine",
    description: "Output the machine settings in the header of the code.",
    group      : "formats",
    type       : "boolean",
    value      : true,
    scope      : "post"
  },
  writeTools: {
    title      : "Write tool list",
    description: "Output a tool list in the header of the code.",
    group      : "formats",
    type       : "boolean",
    value      : true,
    scope      : "post"
  },
  preloadTool: {
    title      : "Preload tool",
    description: "Preloads the next tool at a tool change (if any).",
    group      : "preferences",
    type       : "boolean",
    value      : true,
    scope      : "post"
  },
  showSequenceNumbers: {
    title      : "Use sequence numbers",
    description: "'Yes' outputs sequence numbers on each block, 'Only on tool change' outputs sequence numbers on tool change blocks only, and 'No' disables the output of sequence numbers.",
    group      : "formats",
    type       : "enum",
    values     : [
      {title:"Yes", id:"true"},
      {title:"No", id:"false"},
      {title:"Only on tool change", id:"toolChange"}
    ],
    value: "true",
    scope: "post"
  },
  sequenceNumberStart: {
    title      : "Start sequence number",
    description: "The number at which to start the sequence numbers.",
    group      : "formats",
    type       : "integer",
    value      : 10,
    scope      : "post"
  },
  sequenceNumberIncrement: {
    title      : "Sequence number increment",
    description: "The amount by which the sequence number is incremented by in each block.",
    group      : "formats",
    type       : "integer",
    value      : 5,
    scope      : "post"
  },
  optionalStop: {
    title      : "Optional stop",
    description: "Outputs optional stop code during when necessary in the code.",
    group      : "preferences",
    type       : "boolean",
    value      : true,
    scope      : "post"
  },
  separateWordsWithSpace: {
    title      : "Separate words with space",
    description: "Adds spaces between words if 'yes' is selected.",
    group      : "formats",
    type       : "boolean",
    value      : true,
    scope      : "post"
  },
  useRadius: {
    title      : "Radius arcs",
    description: "If yes is selected, arcs are outputted using radius values rather than IJK.",
    group      : "preferences",
    type       : "boolean",
ellyan's avatar
ellyan committed
    value      : false,
ellyan's avatar
ellyan committed
    scope      : "post"
  },
  showNotes: {
    title      : "Show notes",
    description: "Writes operation notes as comments in the outputted code.",
    group      : "formats",
    type       : "boolean",
    value      : false,
    scope      : "post"
  },
  safePositionMethod: {
    title      : "Safe Retracts",
    description: "Select your desired retract option. 'Clearance Height' retracts to the operation clearance height.",
    group      : "homePositions",
    type       : "enum",
    values     : [
      {title:"Clearance Height", id:"clearanceHeight"},
      {title:"G52", id:"G52"}
    ],
    value: "G52",
    scope: "post"
  },
  controllerType: {
    title      : "Controller version",
    description: "Select the family of controller.",
    group      : "configuration",
    type       : "enum",
    values     : [
      {title:"NUM 7xx", id:"NUM7x0"},
      {title:"NUM 10xx", id:"NUM10x0"}
    ],
    value: "NUM7x0",
    scope: "post"
ellyan's avatar
ellyan committed
  },
  tcMoveType: {
    title      : "Safe tool change move",
    description: "Specifies if we need to move to a safe location to change tool and in which order.",
    group      : "toolChange",
    type       : "enum",
    values     : [
      {title:"Disabled", id:"disable"},
      {title:"Simultaneous", id:"all"},
      {title:"Z then XY", id:"ZX"},
      {title:"XY only", id:"XY"}
    ],
    value: "disable",
    scope: "post"
  },
  tcPositionX: {
    title      : "G52 Tool Change position X",
    description: "G52 X-axis Tool Change position.",
    group      : "toolChange",
    type       : "number",
    value      : 0,
    scope      : "post"
  },
  tcPositionY: {
    title      : "G52 Tool Change position Y",
    description: "G52 Y-axis Tool Change position.",
    group      : "toolChange",
    type       : "number",
    value      : 0,
    scope      : "post"
  },
  tcPositionZ: {
    title      : "G52 Tool Change position Z",
    description: "G52 Z-axis Tool Change position.",
    group      : "toolChange",
    type       : "number",
    value      : 0,
    scope      : "post"
ellyan's avatar
ellyan committed
  }
};

// wcs definiton
wcsDefinitions = {
  useZeroOffset: false,
  wcs          : [
    {name:"Standard", format:"#", range:[1, 1]}
  ]
};

var gFormat = createFormat({prefix:"G", width:2, zeropad:true, decimals:0});
var mFormat = createFormat({prefix:"M", width:2, zeropad:true, decimals:0});
var hFormat = createFormat({prefix:"H", width:2, zeropad:true, decimals:0});
ellyan's avatar
ellyan committed
var diameterOffsetFormat = createFormat({prefix:"D", width:2, zeropad:true, decimals:0});
ellyan's avatar
ellyan committed

var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true});
var rFormat = xyzFormat; // radius
var abcFormat = createFormat({decimals:3, forceDecimal:true, scale:DEG});
var feedFormat = createFormat({decimals:(unit == MM ? 0 : 1), forceDecimal:true});
ellyan's avatar
ellyan committed
var pitchFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true});
ellyan's avatar
ellyan committed
var toolFormat = createFormat({width:2, zeropad:true, decimals:0});
var rpmFormat = createFormat({decimals:0});
var secFormat = createFormat({decimals:2, forceDecimal:true}); // seconds - range 0.01-99.99
var taperFormat = createFormat({decimals:1, scale:DEG});

var xOutput = createVariable({prefix:"X"}, xyzFormat);
var yOutput = createVariable({prefix:"Y"}, xyzFormat);
var zOutput = createVariable({onchange:function () {retracted = false;}, prefix:"Z"}, xyzFormat);
var aOutput = createVariable({prefix:"A"}, abcFormat);
var bOutput = createVariable({prefix:"B"}, abcFormat);
var cOutput = createVariable({prefix:"C"}, abcFormat);
var feedOutput = createVariable({prefix:"F"}, feedFormat);
ellyan's avatar
ellyan committed
var pitchOutput = createVariable({prefix:"K", force:true}, pitchFormat);
ellyan's avatar
ellyan committed
var sOutput = createVariable({prefix:"S", force:true}, rpmFormat);

// circular output
var iOutput = createVariable({prefix:"I", force:true}, xyzFormat);
var jOutput = createVariable({prefix:"J", force:true}, xyzFormat);
var kOutput = createVariable({prefix:"K", force:true}, xyzFormat);

var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ...
var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19
var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91
var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94
var gUnitModal = createModal({}, gFormat); // modal group 6 // G70-71
var gCycleModal = createModal({}, gFormat); // modal group 9 // G81, ...
var gRetractModal = createModal({}, gFormat); // modal group 10 // G98-99
ellyan's avatar
ellyan committed
var mClampModal = createModalGroup(
  {strict:false},
  [
    [10, 11] // 4th axis clamp / unclamp
    //[12, 13] // 5th axis clamp / unclamp
  ],
  mFormat
);

var settings = {
  coolant: {
    // samples:
    // {id: COOLANT_THROUGH_TOOL, on: 88, off: 89}
    // {id: COOLANT_THROUGH_TOOL, on: [8, 88], off: [9, 89]}
    // {id: COOLANT_THROUGH_TOOL, on: "M88 P3 (myComment)", off: "M89"}
    coolants: [
      {id:COOLANT_FLOOD, on:8},
      {id:COOLANT_MIST, on:7},
      {id:COOLANT_THROUGH_TOOL},
      {id:COOLANT_AIR},
      {id:COOLANT_AIR_THROUGH_TOOL},
      {id:COOLANT_SUCTION},
      {id:COOLANT_FLOOD_MIST},
      {id:COOLANT_FLOOD_THROUGH_TOOL},
      {id:COOLANT_OFF, off:9}
    ],
    singleLineCoolant: false, // specifies to output multiple coolant codes in one line rather than in separate lines
  },
  retract: {
    cancelRotationOnRetracting: false, // specifies that rotations (G68) need to be canceled prior to retracting
    methodXY                  : undefined, // special condition, overwrite retract behavior per axis
    methodZ                   : undefined, // special condition, overwrite retract behavior per axis
    useZeroValues             : ["G28", "G30"] // enter property value id(s) for using "0" value instead of machineConfiguration axes home position values (ie G30 Z0)
  },
  machineAngles: { // refer to https://cam.autodesk.com/posts/reference/classMachineConfiguration.html#a14bcc7550639c482492b4ad05b1580c8
    controllingAxis: ABC,
    type           : PREFER_PREFERENCE,
    options        : ENABLE_ALL
  },
  workPlaneMethod: {
    useTiltedWorkplane    : false, // specifies that tilted workplanes should be used (ie. G68.2, G254, PLANE SPATIAL, CYCLE800), can be overwritten by property
    eulerConvention       : EULER_ZXZ_R, // specifies the euler convention (ie EULER_XYZ_R), set to undefined to use machine angles for TWP commands ('undefined' requires machine configuration)
    eulerCalculationMethod: "standard", // ('standard' / 'machine') 'machine' adjusts euler angles to match the machines ABC orientation, machine configuration required
    cancelTiltFirst       : true, // cancel tilted workplane prior to WCS (G54-G59) blocks
    useABCPrepositioning  : false, // position ABC axes prior to tilted workplane blocks
    forceMultiAxisIndexing: false, // force multi-axis indexing for 3D programs
    optimizeType          : undefined // can be set to OPTIMIZE_NONE, OPTIMIZE_BOTH, OPTIMIZE_TABLES, OPTIMIZE_HEADS, OPTIMIZE_AXIS. 'undefined' uses legacy rotations
  },
  comments: {
    permittedCommentChars: " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-:",
    prefix               : "(", // specifies the prefix for the comment
    suffix               : ")", // specifies the suffix for the comment
    upperCase            : true, // set to true to output all comments in upper case
    maximumLineLength    : 80, // the maximum number of charaters allowed in a line, set to 0 to disable comment output
  },
  maximumSequenceNumber       : undefined, // the maximum sequence number (Nxxx), use 'undefined' for unlimited
  supportsOptionalBlocks      : false, // specifies if optional block output is supported
  supportsTCP                 : false, // specifies if the postprocessor does support TCP
  outputToolLengthCompensation: false, // specifies if tool length compensation code should be output (G43)
  outputToolLengthOffset      : false // specifies if tool length offset code should be output (Hxx)
};
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
function writeProgramNumber() {
ellyan's avatar
ellyan committed
  if (programName) {
    var programId;
    try {
      programId = getAsInt(programName);
    } catch (e) {
      error(localize("Program name must be a number."));
      return;
    }
    if (programId > 9000) {
      warning(localize("Using NUM reserved program number."));
    }
ellyan's avatar
ellyan committed
    writeln("%" + programId + conditional(programComment, " " + formatComment(programComment)));
ellyan's avatar
ellyan committed
  } else {
    writeln("%");
  }
ellyan's avatar
ellyan committed
}
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
function onOpen() {
  // define and enable machine configuration
  receivedMachineConfiguration = machineConfiguration.isReceived();
  if (typeof defineMachine == "function") {
    defineMachine(); // hardcoded machine configuration
ellyan's avatar
ellyan committed
  }
ellyan's avatar
ellyan committed
  activateMachine(); // enable the machine optimizations and settings
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  if (getProperty("useRadius")) {
    maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC
  }
  if (!getProperty("separateWordsWithSpace")) {
    setWordSeparator("");
ellyan's avatar
ellyan committed
  }

ellyan's avatar
ellyan committed
  writeProgramNumber();
  writeProgramHeader();
ellyan's avatar
ellyan committed
  // absolute coordinates and feed per min
  writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17));
ellyan's avatar
ellyan committed
  writeBlock(gUnitModal.format(unit == MM ? 71 : 70));
  validateCommonParameters();
ellyan's avatar
ellyan committed
}

function onParameter(name, value) {
}

ellyan's avatar
ellyan committed
function onSection() {
  var forceSectionRestart = optionalSection && !currentSection.isOptional();
  optionalSection = currentSection.isOptional();
  var insertToolCall = isToolChangeNeeded("number") || forceSectionRestart;
  var newWorkOffset = isNewWorkOffset() || forceSectionRestart;
  var newWorkPlane = isNewWorkPlane() || forceSectionRestart;
  if (insertToolCall || newWorkOffset || newWorkPlane) {
    if (insertToolCall && !isFirstSection()) {
      onCommand(COMMAND_STOP_SPINDLE); // stop spindle before retract during tool change
ellyan's avatar
ellyan committed
    }
ellyan's avatar
ellyan committed
    writeRetract(Z); // retract
ellyan's avatar
ellyan committed
  }

ellyan's avatar
ellyan committed
  writeComment(getParameter("operation-comment", ""));
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  if (getProperty("showNotes")) {
    writeSectionNotes();
ellyan's avatar
ellyan committed
  }

ellyan's avatar
ellyan committed
  // tool change
  writeToolCall(tool, insertToolCall);
  startSpindle(tool, insertToolCall);
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  // Output modal commands here
  writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17));
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  forceXYZ();
  var abc = defineWorkPlane(currentSection, true);
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  setCoolant(tool.coolant); // writes the required coolant codes
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  forceAny();
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
  // prepositioning
  var initialPosition = getFramePosition(currentSection.getInitialPosition());
  var isRequired = insertToolCall || retracted || !lengthCompensationActive  || (!isFirstSection() && getPreviousSection().isMultiAxis());
  writeInitialPositioning(initialPosition, isRequired);
}
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
function onDwell(seconds) {
  if (seconds > 99.99) {
    warning(localize("Dwelling time is out of range."));
ellyan's avatar
ellyan committed
  }
ellyan's avatar
ellyan committed
  seconds = clamp(0.01, seconds, 99.99);
  writeBlock(gFormat.format(4), "F" + secFormat.format(seconds));
ellyan's avatar
ellyan committed
}

ellyan's avatar
ellyan committed
function onSpindleSpeed(spindleSpeed) {
  writeBlock(sOutput.format(spindleSpeed));
ellyan's avatar
ellyan committed
}

ellyan's avatar
ellyan committed
function onCycle() {
  writeBlock(gPlaneModal.format(17));
}
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
function getCommonCycle(x, y, z, er, cl) {
  forceXYZ();
  if (getProperty("controllerType") == "NUM10x0") {
    return [xOutput.format(x), yOutput.format(y),
      zOutput.format(z),
      "EH" + xyzFormat.format(er),
      "ER" + xyzFormat.format(cl)];
  } else {
    return [xOutput.format(x), yOutput.format(y),
      zOutput.format(z),
      "ER" + xyzFormat.format(er)];
ellyan's avatar
ellyan committed
  }
ellyan's avatar
ellyan committed
}
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
function onCyclePoint(x, y, z) {
  var forward;
  if (currentSection.isOptimizedForMachine()) {
    forward = machineConfiguration.getOptimizedDirection(currentSection.workPlane.forward, getCurrentDirection(), false, false);
  } else {
    forward = getRotation().forward;
ellyan's avatar
ellyan committed
  }

ellyan's avatar
ellyan committed
  if (!isSameDirection(forward, new Vector(0, 0, 1))) {
    expandCyclePoint(x, y, z);
    return;
  }
  // check if we can group the drilling or not
  var cycleRequiresRetract = xyzFormat.areDifferent(cycle.retract, cycle.clearance);
  if (getProperty("controllerType") == "NUM10x0") {
    cycleRequiresRetract = false; // retract handled by EH/ER
ellyan's avatar
ellyan committed
  }

ellyan's avatar
ellyan committed
  if (isFirstCyclePoint() || cycleRequiresRetract) {
    repositionToCycleClearance(cycle, getCurrentPosition().x, getCurrentPosition().y, z);
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
    // return to initial Z which is clearance plane and set absolute mode
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
    var F = cycle.feedrate;
    var EF = (cycle.dwell == 0) ? 0 : clamp(0.01, cycle.dwell, 99.99); // in seconds
ellyan's avatar
ellyan committed

ellyan's avatar
ellyan committed
    switch (cycleType) {
    case "drilling":
ellyan's avatar
ellyan committed
      writeBlock(
ellyan's avatar
ellyan committed
        gAbsIncModal.format(90), gCycleModal.format(81),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        feedOutput.format(F)
ellyan's avatar
ellyan committed
      );
      break;
    case "counter-boring":
      if (EF > 0) {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(82),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          "EF" + secFormat.format(EF),
          feedOutput.format(F)
        );
      } else {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(81),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          feedOutput.format(F)
        );
      }
      break;
    case "chip-breaking":
      if (cycle.accumulatedDepth < cycle.depth) {
        expandCyclePoint(x, y, z);
      } else {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(87),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          "P" + xyzFormat.format(cycle.incrementalDepth),
          conditional(EF > 0, "EF" + secFormat.format(EF)),
          feedOutput.format(F)
        );
      }
      break;
    case "deep-drilling":
      writeBlock(
        gAbsIncModal.format(90), gCycleModal.format(83),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        "P" + xyzFormat.format(cycle.incrementalDepth),
        conditional(EF > 0, "EF" + secFormat.format(EF)),
        feedOutput.format(F)
      );
      break;
    case "tapping":
ellyan's avatar
ellyan committed
      writeBlock(
        gAbsIncModal.format(90), gCycleModal.format(84),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        pitchOutput.format(tool.threadPitch)
      );
      break
ellyan's avatar
ellyan committed
    case "left-tapping":
    case "right-tapping":
      if (!F) {
        F = tool.getTappingFeedrate();
      }
      writeBlock(
        gAbsIncModal.format(90), gCycleModal.format(84),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        "EF" + secFormat.format(EF),
        feedOutput.format(F)
      );
      break;
    case "fine-boring":
      writeBlock(
        gAbsIncModal.format(90), gCycleModal.format(86),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        // "EC" + xyzFormat.format(...),
        feedOutput.format(F)
      );
      break;
    case "reaming":
      if (EF > 0) {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(89),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          "EF" + secFormat.format(EF),
          feedOutput.format(F)
        );
      } else {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(85),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          feedOutput.format(F),
          "EF" + feedFormat.format(cycle.retractFeedrate)
        );
      }
      break;
    case "stop-boring":
      if (EF > 0) {
        expandCyclePoint(x, y, z);
      } else {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(86),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          feedOutput.format(F)
        );
      }
      break;
    case "manual-boring":
      writeBlock(
        gAbsIncModal.format(90), gCycleModal.format(88),
        getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
        "EF" + secFormat.format(EF), // not optional
        feedOutput.format(F)
      );
      break;
    case "boring":
      if (EF > 0) {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(89),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          "EF" + secFormat.format(EF),
          feedOutput.format(F)
        );
      } else {
        writeBlock(
          gAbsIncModal.format(90), gCycleModal.format(85),
          getCommonCycle(x, y, z, cycle.retract, cycle.clearance),
          feedOutput.format(F),
          "EF" + feedFormat.format(cycle.retractFeedrate)
        );
      }
      break;
    default:
      expandCyclePoint(x, y, z);
    }
    if (cycleRequiresRetract && !cycleExpanded) {
      writeBlock(gCycleModal.format(80));
      gMotionModal.reset();
    }
  } else {
    if (cycleExpanded) {
      expandCyclePoint(x, y, z);
    } else {
      writeBlock(xOutput.format(x), yOutput.format(y));
    }
  }
}

function onCycleEnd() {
  if (!cycleExpanded) {
    writeBlock(gCycleModal.format(80));
    zOutput.reset();
  }
}

function onLinear(_x, _y, _z, feed) {
  var x = xOutput.format(_x);
  var y = yOutput.format(_y);
  var z = zOutput.format(_z);
  var f = feedOutput.format(feed);
  if (x || y || z) {
    if (pendingRadiusCompensation >= 0) {
      pendingRadiusCompensation = -1;
ellyan's avatar
ellyan committed
      var d = diameterOffsetFormat.format(tool.diameterOffset);
ellyan's avatar
ellyan committed
      switch (radiusCompensation) {
      case RADIUS_COMPENSATION_LEFT:
ellyan's avatar
ellyan committed
        writeBlock(gPlaneModal.format(17), d, gMotionModal.format(1), gFormat.format(41), x, y, z, f);
ellyan's avatar
ellyan committed
        break;
      case RADIUS_COMPENSATION_RIGHT:
ellyan's avatar
ellyan committed
        writeBlock(gPlaneModal.format(17), d, gMotionModal.format(1), gFormat.format(42), x, y, z, f);
ellyan's avatar
ellyan committed
        break;
      default:
        writeBlock(gMotionModal.format(1), gFormat.format(40), x, y, z, f);
      }
    } else {
      writeBlock(gMotionModal.format(1), x, y, z, f);
    }
  } else if (f) {
    if (getNextRecord().isMotion()) { // try not to output feed without motion
      feedOutput.reset(); // force feed on next line
    } else {
      writeBlock(gMotionModal.format(1), f);
    }
  }
}

function onCircular(clockwise, cx, cy, cz, x, y, z, feed) {
  if (pendingRadiusCompensation >= 0) {
    error(localize("Radius compensation cannot be activated/deactivated for a circular move."));
    return;
  }
  switch (getCircularPlane()) {
  case PLANE_XY:
    xOutput.reset();
    yOutput.reset();
    break;
  case PLANE_ZX:
    xOutput.reset();
    zOutput.reset();
    break;
  case PLANE_YZ:
    yOutput.reset();
    zOutput.reset();
    break;
  }

  // the maximum allowed radius difference is 20um
  if (isFullCircle()) {
    if (getProperty("useRadius") || isHelical()) { // radius mode does not support full arcs
      linearize(tolerance);
      return;
    }
    switch (getCircularPlane()) {
    case PLANE_XY:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx), jOutput.format(cy), feedOutput.format(feed));
      break;
    case PLANE_ZX:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx), kOutput.format(cz), feedOutput.format(feed));
      break;
    case PLANE_YZ:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), jOutput.format(cy), kOutput.format(cz), feedOutput.format(feed));
      break;
    default:
      linearize(tolerance);
    }
  } else if (!getProperty("useRadius")) {
    switch (getCircularPlane()) {
    case PLANE_XY:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx), jOutput.format(cy), conditional(isHelical(), "K" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    case PLANE_ZX:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx), kOutput.format(cz), conditional(isHelical(), "J" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    case PLANE_YZ:
      writeBlock(gAbsIncModal.format(90), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy), kOutput.format(cz), conditional(isHelical(), "I" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    default:
      linearize(tolerance);
    }
  } else { // use radius mode
    var r = getCircularRadius();
    if (toDeg(getCircularSweep()) > (180 + 1e-9)) {
      error(localize("Circular interpolation over 180 degrees cannot be handled by radius programming. Set property 'Radius arcs' to 'No'."));
      return;
    }
    switch (getCircularPlane()) {
    case PLANE_XY:
      writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), conditional(isHelical(), "K" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    case PLANE_ZX:
      writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), conditional(isHelical(), "J" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    case PLANE_YZ:
      writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), conditional(isHelical(), "I" + xyzFormat.format(getHelicalPitch())), feedOutput.format(feed));
      break;
    default:
      linearize(tolerance);
    }
  }
}

var mapCommand = {
ellyan's avatar
ellyan committed
  COMMAND_END              : 2,
  COMMAND_STOP_SPINDLE     : 5,
  COMMAND_ORIENTATE_SPINDLE: 19
ellyan's avatar
ellyan committed
};

function onCommand(command) {
  switch (command) {
ellyan's avatar
ellyan committed
  case COMMAND_COOLANT_OFF:
    setCoolant(COOLANT_OFF);
    return;
  case COMMAND_COOLANT_ON:
    setCoolant(tool.coolant);
    return;
ellyan's avatar
ellyan committed
  case COMMAND_STOP:
    writeBlock(mFormat.format(0));
    forceSpindleSpeed = true;
    forceCoolant = true;
    return;
  case COMMAND_OPTIONAL_STOP:
    writeBlock(mFormat.format(1));
    forceSpindleSpeed = true;
    forceCoolant = true;
    return;
  case COMMAND_START_SPINDLE:
ellyan's avatar
ellyan committed
    forceSpindleSpeed = false;
    writeBlock(gFormat.format(97), sOutput.format(spindleSpeed), mFormat.format(tool.clockwise ? 3 : 4));
    return;
  case COMMAND_LOAD_TOOL:
    //writeToolBlock("T" + toolFormat.format(tool.number), mFormat.format(6));
    writeToolBlock("T" + toolFormat.format(tool.number), ("D" + toolFormat.format(tool.diameterOffset)), mFormat.format(6));
    writeComment(tool.comment);

    var preloadTool = getNextTool(tool.number != getFirstTool().number);
    if (getProperty("preloadTool") && preloadTool) {
      writeBlock("T" + toolFormat.format(preloadTool.number)); // preload next/first tool
    }
ellyan's avatar
ellyan committed
    return;
  case COMMAND_LOCK_MULTI_AXIS:
ellyan's avatar
ellyan committed
    writeBlock(mClampModal.format(10));
ellyan's avatar
ellyan committed
    return;
  case COMMAND_UNLOCK_MULTI_AXIS:
ellyan's avatar
ellyan committed
    writeBlock(mClampModal.format(11));
ellyan's avatar
ellyan committed
    return;
  case COMMAND_BREAK_CONTROL:
    return;
  case COMMAND_TOOL_MEASURE:
    return;
  }

  var stringId = getCommandStringId(command);
  var mcode = mapCommand[stringId];
  if (mcode != undefined) {
    writeBlock(mFormat.format(mcode));
  } else {
    onUnsupportedCommand(command);
  }
}

function onSectionEnd() {
  if (currentSection.isMultiAxis()) {
    writeBlock(gMotionModal.format(49));
  }
  if (!isLastSection() && (getNextSection().getTool().coolant != tool.coolant)) {
    setCoolant(COOLANT_OFF);
  }
  writeBlock(gPlaneModal.format(17));
  forceAny();
}

function writeRetract() {
ellyan's avatar
ellyan committed
  var retract = getRetractParameters.apply(this, arguments);
  if (retract && retract.words.length > 0) {
    if (typeof gRotationModal != "undefined" && gRotationModal.getCurrent() == 68 && settings.retract.cancelRotationOnRetracting) { // cancel rotation before retracting
      cancelWorkPlane(true);
    }
    switch (retract.method) {
    case "G52":
      gMotionModal.reset();
      writeBlock(gAbsIncModal.format(90), gFormat.format(52), gMotionModal.format(0), retract.words);
      break;
    default:
      error(localize("Unsupported safe position method."));
      return;
    }
  }
}

function onClose() {
  setCoolant(COOLANT_OFF);
  writeRetract(Z);
  setWorkPlane(new Vector(0, 0, 0)); // reset working plane
  writeRetract(X, Y);
  writeBlock(mFormat.format(2)); // stop program
  writeln(DC3); // XOFF
}

// >>>>> INCLUDED FROM include_files/commonFunctions.cpi
// internal variables, do not change
var receivedMachineConfiguration;
var tcp = {isSupportedByControl:getSetting("supportsTCP", true), isSupportedByMachine:false, isSupportedByOperation:false};
var multiAxisFeedrate;
var sequenceNumber;
var optionalSection = false;
var currentWorkOffset;
var forceSpindleSpeed = false;
var retracted = false; // specifies that the tool has been retracted to the safe plane
var operationNeedsSafeStart = false; // used to convert blocks to optional for safeStartAllOperations

function activateMachine() {
  // disable unsupported rotary axes output
  if (!machineConfiguration.isMachineCoordinate(0) && (typeof aOutput != "undefined")) {
    aOutput.disable();
  }
  if (!machineConfiguration.isMachineCoordinate(1) && (typeof bOutput != "undefined")) {
    bOutput.disable();
  }
  if (!machineConfiguration.isMachineCoordinate(2) && (typeof cOutput != "undefined")) {
    cOutput.disable();
  }

  // setup usage of useTiltedWorkplane
  settings.workPlaneMethod.useTiltedWorkplane = getProperty("useTiltedWorkplane") != undefined ? getProperty("useTiltedWorkplane") :
    getSetting("workPlaneMethod.useTiltedWorkplane", false);
  settings.workPlaneMethod.useABCPrepositioning = getProperty("useABCPrepositioning") != undefined ? getProperty("useABCPrepositioning") :
    getSetting("workPlaneMethod.useABCPrepositioning", false);

  if (!machineConfiguration.isMultiAxisConfiguration()) {
    return; // don't need to modify any settings for 3-axis machines
  }

  // identify if any of the rotary axes has TCP enabled
  var axes = [machineConfiguration.getAxisU(), machineConfiguration.getAxisV(), machineConfiguration.getAxisW()];
  tcp.isSupportedByMachine = axes.some(function(axis) {return axis.isEnabled() && axis.isTCPEnabled();}); // true if TCP is enabled on any rotary axis

  // save multi-axis feedrate settings from machine configuration
  var mode = machineConfiguration.getMultiAxisFeedrateMode();
  var type = mode == FEED_INVERSE_TIME ? machineConfiguration.getMultiAxisFeedrateInverseTimeUnits() :
    (mode == FEED_DPM ? machineConfiguration.getMultiAxisFeedrateDPMType() : DPM_STANDARD);
  multiAxisFeedrate = {
    mode     : mode,
    maximum  : machineConfiguration.getMultiAxisFeedrateMaximum(),
    type     : type,
    tolerance: mode == FEED_DPM ? machineConfiguration.getMultiAxisFeedrateOutputTolerance() : 0,
    bpwRatio : mode == FEED_DPM ? machineConfiguration.getMultiAxisFeedrateBpwRatio() : 1
  };

  // setup of retract/reconfigure  TAG: Only needed until post kernel supports these machine config settings
  if (receivedMachineConfiguration && machineConfiguration.performRewinds()) {
    safeRetractDistance = machineConfiguration.getSafeRetractDistance();
    safePlungeFeed = machineConfiguration.getSafePlungeFeedrate();
    safeRetractFeed = machineConfiguration.getSafeRetractFeedrate();
  }
  if (typeof safeRetractDistance == "number" && getProperty("safeRetractDistance") != undefined && getProperty("safeRetractDistance") != 0) {
    safeRetractDistance = getProperty("safeRetractDistance");
  }

  if (machineConfiguration.isHeadConfiguration()) {
    compensateToolLength = typeof compensateToolLength == "undefined" ? false : compensateToolLength;
  }

  if (machineConfiguration.isHeadConfiguration() && compensateToolLength) {
    for (var i = 0; i < getNumberOfSections(); ++i) {
      var section = getSection(i);
      if (section.isMultiAxis()) {
        machineConfiguration.setToolLength(getBodyLength(section.getTool())); // define the tool length for head adjustments
        section.optimizeMachineAnglesByMachine(machineConfiguration, OPTIMIZE_AXIS);
      }
    }
  } else {
    optimizeMachineAngles2(OPTIMIZE_AXIS);
  }
}

function getBodyLength(tool) {
  for (var i = 0; i < getNumberOfSections(); ++i) {
    var section = getSection(i);
    if (tool.number == section.getTool().number) {
      return section.getParameter("operation:tool_overallLength", tool.bodyLength + tool.holderLength);
    }
  }
  return tool.bodyLength + tool.holderLength;
}

function getFeed(f) {
  if (getProperty("useG95")) {
    return feedOutput.format(f / spindleSpeed); // use feed value
  }
  if (typeof activeMovements != "undefined" && activeMovements) {
    var feedContext = activeMovements[movement];
    if (feedContext != undefined) {
      if (!feedFormat.areDifferent(feedContext.feed, f)) {
        if (feedContext.id == currentFeedId) {
          return ""; // nothing has changed
        }
        forceFeed();
        currentFeedId = feedContext.id;
        return settings.parametricFeeds.feedOutputVariable + (settings.parametricFeeds.firstFeedParameter + feedContext.id);
      }
    }
    currentFeedId = undefined; // force parametric feed next time
  }
  return feedOutput.format(f); // use feed value
}

function validateCommonParameters() {
  validateToolData();
  for (var i = 0; i < getNumberOfSections(); ++i) {
    var section = getSection(i);
    if (getSection(0).workOffset == 0 && section.workOffset > 0) {
      error(localize("Using multiple work offsets is not possible if the initial work offset is 0."));
    }
    if (section.isMultiAxis()) {
      if (!section.isOptimizedForMachine() && !getSetting("supportsToolVectorOutput", false)) {
        error(localize("This postprocessor requires a machine configuration for 5-axis simultaneous toolpath."));
      }
      if (machineConfiguration.getMultiAxisFeedrateMode() == FEED_INVERSE_TIME && !getSetting("supportsInverseTimeFeed", true)) {
        error(localize("This postprocessor does not support inverse time feedrates."));
      }
    }
  }
  if (!tcp.isSupportedByControl && tcp.isSupportedByMachine) {
    error(localize("The machine configuration has TCP enabled which is not supported by this postprocessor."));
  }
  if (getProperty("safePositionMethod") == "clearanceHeight") {
    var msg = "-Attention- Property 'Safe Retracts' is set to 'Clearance Height'." + EOL +
      "Ensure the clearance height will clear the part and or fixtures." + EOL +
      "Raise the Z-axis to a safe height before starting the program.";
    warning(msg);
    writeComment(msg);
  }
}

function validateToolData() {
  var _default = 99999;
  var _maximumSpindleRPM = machineConfiguration.getMaximumSpindleSpeed() > 0 ? machineConfiguration.getMaximumSpindleSpeed() :
    settings.maximumSpindleRPM == undefined ? _default : settings.maximumSpindleRPM;
  var _maximumToolNumber = machineConfiguration.isReceived() && machineConfiguration.getNumberOfTools() > 0 ? machineConfiguration.getNumberOfTools() :
    settings.maximumToolNumber == undefined ? _default : settings.maximumToolNumber;
  var _maximumToolLengthOffset = settings.maximumToolLengthOffset == undefined ? _default : settings.maximumToolLengthOffset;
  var _maximumToolDiameterOffset = settings.maximumToolDiameterOffset == undefined ? _default : settings.maximumToolDiameterOffset;

  var header = ["Detected maximum values are out of range.", "Maximum values:"];
  var warnings = {
    toolNumber    : {msg:"Tool number value exceeds the maximum value for tool: " + EOL, max:" Tool number: " + _maximumToolNumber, values:[]},
    lengthOffset  : {msg:"Tool length offset value exceeds the maximum value for tool: " + EOL, max:" Tool length offset: " + _maximumToolLengthOffset, values:[]},
    diameterOffset: {msg:"Tool diameter offset value exceeds the maximum value for tool: " + EOL, max:" Tool diameter offset: " + _maximumToolDiameterOffset, values:[]},
    spindleSpeed  : {msg:"Spindle speed exceeds the maximum value for operation: " + EOL, max:" Spindle speed: " + _maximumSpindleRPM, values:[]}
  };

  var toolIds = [];
  for (var i = 0; i < getNumberOfSections(); ++i) {
    var section = getSection(i);
    if (toolIds.indexOf(section.getTool().getToolId()) === -1) { // loops only through sections which have a different tool ID
      var toolNumber = section.getTool().number;
      var lengthOffset = section.getTool().lengthOffset;
      var diameterOffset = section.getTool().diameterOffset;
      var comment = section.getParameter("operation-comment", "");

      if (toolNumber > _maximumToolNumber && !getProperty("toolAsName")) {
        warnings.toolNumber.values.push(SP + toolNumber + EOL);
      }
      if (lengthOffset > _maximumToolLengthOffset) {
        warnings.lengthOffset.values.push(SP + "Tool " + toolNumber + " (" + comment + "," + " Length offset: " + lengthOffset + ")" + EOL);
      }
      if (diameterOffset > _maximumToolDiameterOffset) {
        warnings.diameterOffset.values.push(SP + "Tool " + toolNumber + " (" + comment + "," + " Diameter offset: " + diameterOffset + ")" + EOL);
      }
      toolIds.push(section.getTool().getToolId());
    }
    // loop through all sections regardless of tool id for idenitfying spindle speeds

    // identify if movement ramp is used in current toolpath, use ramp spindle speed for comparisons
    var ramp = section.getMovements() & ((1 << MOVEMENT_RAMP) | (1 << MOVEMENT_RAMP_ZIG_ZAG) | (1 << MOVEMENT_RAMP_PROFILE) | (1 << MOVEMENT_RAMP_HELIX));
    var _sectionSpindleSpeed = Math.max(section.getTool().spindleRPM, ramp ? section.getTool().rampingSpindleRPM : 0, 0);
    if (_sectionSpindleSpeed > _maximumSpindleRPM) {
      warnings.spindleSpeed.values.push(SP + section.getParameter("operation-comment", "") + " (" + _sectionSpindleSpeed + " RPM" + ")" + EOL);
    }
  }

  // sort lists by tool number
  warnings.toolNumber.values.sort(function(a, b) {return a - b;});
  warnings.lengthOffset.values.sort(function(a, b) {return a.localeCompare(b);});
  warnings.diameterOffset.values.sort(function(a, b) {return a.localeCompare(b);});

  var warningMessages = [];
  for (var key in warnings) {
    if (warnings[key].values != "") {
      header.push(warnings[key].max); // add affected max values to the header
      warningMessages.push(warnings[key].msg + warnings[key].values.join(""));
    }
  }
  if (warningMessages.length != 0) {
    warningMessages.unshift(header.join(EOL) + EOL);
    warning(warningMessages.join(EOL));
  }
}

function forceFeed() {
  currentFeedId = undefined;
  feedOutput.reset();
}

/** Force output of X, Y, and Z. */
function forceXYZ() {
  xOutput.reset();
  yOutput.reset();
  zOutput.reset();
}

/** Force output of A, B, and C. */
function forceABC() {