586,077 active members*
4,039 visitors online*
Register for free
Login
IndustryArena Forum > MetalWorking Machines > Tormach Personal CNC Mill > Tormach PathPilot™ > Latest Path Pilot Post processor for Fusion 360.
Page 1 of 2 12
Results 1 to 20 of 22
  1. #1
    Join Date
    Jun 2004
    Posts
    6618

    Latest Path Pilot Post processor for Fusion 360.

    What do you guys use that use Fusion 360?
    I don't find anything usable in the Fusion offerings.
    WTH am I missing?
    For the Tormach PCNC1100 series 3.
    Thanks.
    Lee

  2. #2
    Join Date
    Dec 2013
    Posts
    267

    Re: Latest Path Pilot Post processor for Fusion 360.

    Take a look here: Autodesk CAM | Post Library

    EDIT: Scratch that - so sorry to be the guy that just links junk without fully reading it.

    I honestly wasn't able to find the post online! /gasp There is the Generic Mach3Mill post included with Fusion360 under "Use Generic Posts", but I have never tried it.

    I have never had a single hiccup with this post, it works great.

    Tormach_PathPilotBeta_20150701.cps
    Code:
    /**
      Copyright (C) 2012-2014 by Autodesk, Inc.
      All rights reserved.
    
      Tormach Path Pilot post processor configuration.
    
      $Revision: 37254 $
      $Date: 2014-05-26 11:17:00 +0200 (ma, 26 maj 2014) $
      
      FORKID {AE2102AB-B86A-4aa7-8E9B-F0B6935D4E9F}
    */
    //setWriteStack(true);
    description = "Generic Tormach Path Pilot";
    vendor = "Autodesk, Inc.";
    vendorUrl = "http://www.autodesk.com";
    legal = "Copyright (C) 2012-2014 by Autodesk, Inc.";
    certificationLevel = 2;
    minimumRevision = 24000;
    
    extension = "TAP";
    setCodePage("ascii");
    
    tolerance = spatial(0.002, MM);
    
    minimumChordLength = spatial(0.01, MM);
    minimumCircularRadius = spatial(0.01, MM);
    maximumCircularRadius = spatial(1000, MM);
    minimumCircularSweep = toRad(0.01);
    maximumCircularSweep = toRad(180);
    allowHelicalMoves = true;
    allowedCircularPlanes = undefined; // allow any circular motion
    
    
    
    // user-defined properties
    properties = {
      writeMachine: true, // write machine
      writeTools: true, // writes the tools
      useG28: true, // disable to avoid G28 output
      useM6: true, // disable to avoid M6 output - preload is also disabled when M6 is disabled
      preloadTool: false, // preloads next tool on tool change if any
      showSequenceNumbers: true, // show sequence numbers
      sequenceNumberStart: 10, // first sequence number
      sequenceNumberIncrement: 10, // increment for sequence numbers
      optionalStop: true, // optional stop
      separateWordsWithSpace: true, // specifies that the words should be separated with a white space
      useRadius: true, // specifies that arcs should be output using the radius (R word) instead of the I, J, and K words.
      dwellInSeconds: true // specifies the unit for dwelling: true:seconds and false:milliseconds.
    };
    
    
    
    var permittedCommentChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-";
    
    var mapCoolantTable = new Table(
      [9, 8, 7],
      {initial:COOLANT_OFF, force:true},
      "Invalid coolant mode"
    );
    
    var nFormat = createFormat({prefix:"N", decimals:0});
    var gFormat = createFormat({prefix:"G", decimals:1});
    var mFormat = createFormat({prefix:"M", decimals:0});
    var hFormat = createFormat({prefix:"H", decimals:0});
    var dFormat = createFormat({prefix:"D", decimals:0});
    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});
    var toolFormat = createFormat({decimals:0});
    var rpmFormat = createFormat({decimals:0});
    var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-99999.999
    var milliFormat = createFormat({decimals:0}); // milliseconds // range 1-9999
    var taperFormat = createFormat({decimals:1, scale:DEG});
    
    var xOutput = createVariable({prefix:"X"}, xyzFormat);
    var yOutput = createVariable({prefix:"Y"}, xyzFormat);
    var zOutput = createVariable({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);
    var sOutput = createVariable({prefix:"S", force:true}, rpmFormat);
    var dOutput = createVariable({}, dFormat);
    
    // circular output
    var iOutput = createReferenceVariable({prefix:"I", force:true}, xyzFormat);
    var jOutput = createReferenceVariable({prefix:"J", force:true}, xyzFormat);
    var kOutput = createReferenceVariable({prefix:"K", force:true}, xyzFormat);
    
    var gMotionModal = createModal({force:true}, 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 // G20-21
    var gCycleModal = createModal({force:false}, gFormat); // modal group 9 // G81, ...
    var gRetractModal = createModal({force:true}, gFormat); // modal group 10 // G98-99
    
    var WARNING_WORK_OFFSET = 0;
    
    // collected state
    var sequenceNumber;
    var currentWorkOffset;
    var previousCoolantMode;
    
    /**
      Writes the specified block.
    */
    function writeBlock() {
      if (properties.showSequenceNumbers) {
        writeWords2(nFormat.format(sequenceNumber % 100000), arguments);
        sequenceNumber += properties.sequenceNumberIncrement;
      } else {
        writeWords(arguments);
      }
    }
    
    /**
      Output a comment.
    */
    function writeComment(text) {
      writeln("(" + filterText(String(text).toUpperCase(), permittedCommentChars) + ")");
    }
    
    function onOpen() {
    
      if (false) {
        var aAxis = createAxis({coordinate:0, table:true, axis:[-1, 0, 0], cyclic:true, preference:1});
        machineConfiguration = new MachineConfiguration(aAxis);
    
        setMachineConfiguration(machineConfiguration);
        optimizeMachineAngles2(1); // map tip mode
      }
    
      if (!machineConfiguration.isMachineCoordinate(0)) {
        aOutput.disable();
      }
      if (!machineConfiguration.isMachineCoordinate(1)) {
        bOutput.disable();
      }
      if (!machineConfiguration.isMachineCoordinate(2)) {
        cOutput.disable();
      }
      
      if (!properties.separateWordsWithSpace) {
        setWordSeparator("");
      }
    
      sequenceNumber = properties.sequenceNumberStart;
    
      writeln("%");
      writeln("(*********************************************************)");
      writeln("(* Tormach Path Pilot Post Processsor Version 0.4 Debug  *)");
      writeln("(*********************************************************)");
      if (programName) {
        writeComment(programName);
      }
      if (programComment) {
        writeComment(programComment);
      }
    
      // dump machine configuration
      var vendor = machineConfiguration.getVendor();
      var model = machineConfiguration.getModel();
      var description = machineConfiguration.getDescription();
    
      if (properties.writeMachine && (vendor || model || description)) {
        writeComment(localize("Machine"));
        if (vendor) {
          writeComment("  " + localize("vendor") + ": " + vendor);
        }
        if (model) {
          writeComment("  " + localize("model") + ": " + model);
        }
        if (description) {
          writeComment("  " + localize("description") + ": "  + description);
        }
      }
    
      // dump tool information
      if (properties.writeTools) {
        var zRanges = {};
        if (is3D()) {
          var numberOfSections = getNumberOfSections();
          for (var i = 0; i < numberOfSections; ++i) {
            var section = getSection(i);
            var zRange = section.getGlobalZRange();
            var tool = section.getTool();
            if (zRanges[tool.number]) {
              zRanges[tool.number].expandToRange(zRange);
            } else {
              zRanges[tool.number] = zRange;
            }
          }
        }
    
        var tools = getToolTable();
        if (tools.getNumberOfTools() > 0) {
          for (var i = 0; i < tools.getNumberOfTools(); ++i) {
            var tool = tools.getTool(i);
            var comment = "T" + toolFormat.format(tool.number) + "  " +
              "D=" + xyzFormat.format(tool.diameter) + " " +
              localize("CR") + "=" + xyzFormat.format(tool.cornerRadius);
            if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) {
              comment += " " + localize("TAPER") + "=" + taperFormat.format(tool.taperAngle) + localize("deg");
            }
            if (zRanges[tool.number]) {
              comment += " - " + localize("ZMIN") + "=" + xyzFormat.format(zRanges[tool.number].getMinimum());
            }
            comment += " - " + getToolTypeName(tool.type);
            writeComment(comment);
          }
        }
      }
      
      if (false) {
        // check for duplicate tool number
        for (var i = 0; i < getNumberOfSections(); ++i) {
          var sectioni = getSection(i);
          var tooli = sectioni.getTool();
          for (var j = i + 1; j < getNumberOfSections(); ++j) {
            var sectionj = getSection(j);
            var toolj = sectionj.getTool();
            if (tooli.number == toolj.number) {
              if (xyzFormat.areDifferent(tooli.diameter, toolj.diameter) ||
                  xyzFormat.areDifferent(tooli.cornerRadius, toolj.cornerRadius) ||
                  abcFormat.areDifferent(tooli.taperAngle, toolj.taperAngle) ||
                  (tooli.numberOfFlutes != toolj.numberOfFlutes)) {
                error(
                  subst(
                    localize("Using the same tool number for different cutter geometry for operation '%1' and '%2'."),
                    sectioni.hasParameter("operation-comment") ? sectioni.getParameter("operation-comment") : ("#" + (i + 1)),
                    sectionj.hasParameter("operation-comment") ? sectionj.getParameter("operation-comment") : ("#" + (j + 1))
                  )
                );
                return;
              }
            }
          }
        }
      }
    
      // absolute coordinates and feed per min
      writeBlock(gAbsIncModal.format(90), gFormat.format(54), gFormat.format(64), gFormat.format(50), gPlaneModal.format(17), gFormat.format(40), gFormat.format(80), gFeedModeModal.format(94), gFormat.format(91.1), gFormat.format(49));
    
      switch (unit) {
      case IN:
        writeBlock(gUnitModal.format(20), "(Inch)" );
        break;
      case MM:
        writeBlock(gUnitModal.format(21), "(Metric)");
        break;
      }
    }
    
    function onComment(message) {
      var comments = String(message).split(";");
      for (comment in comments) {
        writeComment(comments[comment]);
      }
    }
    
    /** Force output of X, Y, and Z. */
    function forceXYZ() {
      xOutput.reset();
      yOutput.reset();
      zOutput.reset();
    }
    
    /** Force output of A, B, and C. */
    function forceABC() {
      aOutput.reset();
      bOutput.reset();
      cOutput.reset();
    }
    
    /** Force output of X, Y, Z, A, B, C, and F on next output. */
    function forceAny() {
      forceXYZ();
      forceABC();
      feedOutput.reset();
    }
    
    var currentWorkPlaneABC = undefined;
    
    function forceWorkPlane() {
      currentWorkPlaneABC = undefined;
    }
    
    function setWorkPlane(abc) {
      if (!machineConfiguration.isMultiAxisConfiguration()) {
        return; // ignore
      }
    
      if (!((currentWorkPlaneABC == undefined) ||
            abcFormat.areDifferent(abc.x, currentWorkPlaneABC.x) ||
            abcFormat.areDifferent(abc.y, currentWorkPlaneABC.y) ||
            abcFormat.areDifferent(abc.z, currentWorkPlaneABC.z))) {
        return; // no change
      }
    
      onCommand(COMMAND_UNLOCK_MULTI_AXIS);
    
      // NOTE: add retract here
    
      writeBlock(
        gMotionModal.format(0),
        conditional(machineConfiguration.isMachineCoordinate(0), "A" + abcFormat.format(abc.x)),
        conditional(machineConfiguration.isMachineCoordinate(1), "B" + abcFormat.format(abc.y)),
        conditional(machineConfiguration.isMachineCoordinate(2), "C" + abcFormat.format(abc.z))
      );
      
      onCommand(COMMAND_LOCK_MULTI_AXIS);
    
      currentWorkPlaneABC = abc;
    }
    
    var closestABC = false; // choose closest machine angles
    var currentMachineABC;
    
    function getWorkPlaneMachineABC(workPlane) {
      var W = workPlane; // map to global frame
    
      var abc = machineConfiguration.getABC(W);
      if (closestABC) {
        if (currentMachineABC) {
          abc = machineConfiguration.remapToABC(abc, currentMachineABC);
        } else {
          abc = machineConfiguration.getPreferredABC(abc);
        }
      } else {
        abc = machineConfiguration.getPreferredABC(abc);
      }
      
      try {
        abc = machineConfiguration.remapABC(abc);
        currentMachineABC = abc;
      } catch (e) {
        error(
          localize("Machine angles not supported") + ":"
          + conditional(machineConfiguration.isMachineCoordinate(0), " A" + abcFormat.format(abc.x))
          + conditional(machineConfiguration.isMachineCoordinate(1), " B" + abcFormat.format(abc.y))
          + conditional(machineConfiguration.isMachineCoordinate(2), " C" + abcFormat.format(abc.z))
        );
      }
      
      var direction = machineConfiguration.getDirection(abc);
      if (!isSameDirection(direction, W.forward)) {
        error(localize("Orientation not supported."));
      }
      
      if (!machineConfiguration.isABCSupported(abc)) {
        error(
          localize("Work plane is not supported") + ":"
          + conditional(machineConfiguration.isMachineCoordinate(0), " A" + abcFormat.format(abc.x))
          + conditional(machineConfiguration.isMachineCoordinate(1), " B" + abcFormat.format(abc.y))
          + conditional(machineConfiguration.isMachineCoordinate(2), " C" + abcFormat.format(abc.z))
        );
      }
    
      var tcp = true;
      if (tcp) {
        setRotation(W); // TCP mode
      } else {
        var O = machineConfiguration.getOrientation(abc);
        var R = machineConfiguration.getRemainingOrientation(abc, W);
        setRotation(R);
      }
      
      return abc;
    }
    
    function onSection() {
      var insertToolCall = isFirstSection() ||
        currentSection.getForceToolChange && currentSection.getForceToolChange() ||
        (tool.number != getPreviousSection().getTool().number);
      
      var retracted = false; // specifies that the tool has been retracted to the safe plane
      var newWorkOffset = isFirstSection() ||
        (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes
      var newWorkPlane = isFirstSection() ||
        !isSameDirection(getPreviousSection().getGlobalFinalToolAxis(), currentSection.getGlobalInitialToolAxis());
      if (insertToolCall || newWorkOffset || newWorkPlane) {
        
        if (properties.useG28) {
          // retract to safe plane
          retracted = true;
          //writeBlock(gFormat.format(28), gAbsIncModal.format(91), "Z" + xyzFormat.format(0)); // retract
    	  writeBlock(gFormat.format(30)); // use G30 instead of G28 above
          writeBlock(gAbsIncModal.format(90));
          zOutput.reset();
        }
      }
    
      writeln("");
    
      if (hasParameter("operation-comment")) {
        var comment = getParameter("operation-comment");
        if (comment) {
          writeComment(comment);
        }
      }
    
      if (insertToolCall) {
        forceWorkPlane();
        
        // onCommand(COMMAND_STOP_SPINDLE);
        // onCommand(COMMAND_COOLANT_OFF);
      
        if (!isFirstSection() && properties.optionalStop) {
          onCommand(COMMAND_OPTIONAL_STOP);
        }
    
        if (tool.number > 256) {
          warning(localize("Tool number exceeds maximum value."));
        }
    	
        var lengthOffset = tool.lengthOffset;
        if (lengthOffset > 256) {
          error(localize("Length offset out of range."));
          return;
        }
    
     //   writeBlock(mFormat.format(998));
        writeBlock(gFormat.format(30));
        if (properties.useM6) {
          writeBlock("T" + toolFormat.format(tool.number),
    	  gFormat.format(43),
    	  hFormat.format(lengthOffset),
    	  mFormat.format(6));
        } else {
          writeBlock("T" + toolFormat.format(tool.number), gFormat.format(43), hFormat.format(lengthOffset));
        }
        if (tool.comment) {
          writeComment(tool.comment);
        }
        var showToolZMin = false;
        if (showToolZMin) {
          if (is3D()) {
            var numberOfSections = getNumberOfSections();
            var zRange = currentSection.getGlobalZRange();
            var number = tool.number;
            for (var i = currentSection.getId() + 1; i < numberOfSections; ++i) {
              var section = getSection(i);
              if (section.getTool().number != number) {
                break;
              }
              zRange.expandToRange(section.getGlobalZRange());
            }
            writeComment(localize("ZMIN") + "=" + zRange.getMinimum());
          }
        }
    
        if (properties.preloadTool && properties.useM6) {
          var nextTool = getNextTool(tool.number);
          if (nextTool) {
            writeBlock("T" + toolFormat.format(nextTool.number));
          } else {
            // preload first tool
            var section = getSection(0);
            var firstToolNumber = section.getTool().number;
            if (tool.number != firstToolNumber) {
              writeBlock("T" + toolFormat.format(firstToolNumber));
            }
          }
        }
      }
      
        // set coolant after we have positioned at Z
    	    var c = mapCoolantTable.lookup(tool.coolant);
      /* {
        var c = mapCoolantTable.lookup(tool.coolant);
       if (c) {
          writeBlock(mFormat.format(c));
        } else {
          warning(localize("Coolant not supported."));
        }
      } */
      
     /* if (insertToolCall ||
          isFirstSection() ||
          (rpmFormat.areDifferent(tool.spindleRPM, sOutput.getCurrent())) ||
          (tool.clockwise != getPreviousSection().getTool().clockwise) ||  previousCoolantMode != c) { */
        if (tool.spindleRPM < 1) {
          error(localize("Spindle speed out of range."));
          return;
        }
        if (tool.spindleRPM > 99999) {
          warning(localize("Spindle speed exceeds maximum value."));
        }
        writeBlock(
          sOutput.format(tool.spindleRPM), mFormat.format(tool.clockwise ? 3 : 4),
    	  conditional(c, mFormat.format(c)) 
        );
    	 previousCoolantMode = c;
     // }
    
      // wcs
      var workOffset = currentSection.workOffset;
      if (workOffset == 0) {
        warningOnce(localize("Work offset has not been specified. Using G54 as WCS."), WARNING_WORK_OFFSET);
        workOffset = 1;
      }
      if (workOffset > 0) {
        if (workOffset > 6) {
          var p = workOffset; // 1->... // G59 P1 is the same as G54 and so on
          if (p > 254) {
            error(localize("Work offset out of range."));
          } else {
            if (workOffset != currentWorkOffset) {
              writeBlock(gFormat.format(59), "P" + p); // G59 P
              currentWorkOffset = workOffset;
            }
          }
        } else {
          if (workOffset != currentWorkOffset) {
            writeBlock(gFormat.format(53 + workOffset)); // G54->G59
            currentWorkOffset = workOffset;
          }
        }
      }
    
      forceXYZ();
    
      if (machineConfiguration.isMultiAxisConfiguration()) { // use 5-axis indexing for multi-axis mode
        // set working plane after datum shift
    
        var abc = new Vector(0, 0, 0);
        if (currentSection.isMultiAxis()) {
          forceWorkPlane();
          cancelTransformation();
        } else {
          abc = getWorkPlaneMachineABC(currentSection.workPlane);
        }
        setWorkPlane(abc);
      } else { // pure 3D
        var remaining = currentSection.workPlane;
        if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) {
          error(localize("Tool orientation is not supported."));
          return;
        }
        setRotation(remaining);
      }
    
    
      forceAny();
      gMotionModal.reset();
    
      var initialPosition = getFramePosition(currentSection.getInitialPosition());
      if (!retracted) {
        if (getCurrentPosition().z < initialPosition.z) {
          writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z));
        }
      }
    
      if (!insertToolCall && retracted) { // G43 already called above on tool change
        var lengthOffset = tool.lengthOffset;
        if (lengthOffset > 256) {
          error(localize("Length offset out of range."));
          return;
        }
    
        gMotionModal.reset();
        writeBlock(gPlaneModal.format(17));
        
        if (!machineConfiguration.isHeadConfiguration()) {
          writeBlock(
            gAbsIncModal.format(90),
            gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y)
          );
          writeBlock(gMotionModal.format(0), gFormat.format(43), zOutput.format(initialPosition.z), hFormat.format(lengthOffset));
        } else {
          writeBlock(
            gAbsIncModal.format(90),
            gMotionModal.format(0),
            gFormat.format(43), xOutput.format(initialPosition.x),
            yOutput.format(initialPosition.y),
            zOutput.format(initialPosition.z), hFormat.format(lengthOffset)
          );
        }
      } else {
        writeBlock(
          gAbsIncModal.format(90),
          gMotionModal.format(0),
          xOutput.format(initialPosition.x),
          yOutput.format(initialPosition.y)
        );
      }
    
      // set coolant after we have positioned at Z
     /* {
        var c = mapCoolantTable.lookup(tool.coolant);
        if (c) {
          writeBlock(mFormat.format(c));
        } else {
          warning(localize("Coolant not supported."));
        }
      } */
    }
    
    function onDwell(seconds) {
      if (seconds > 99999.999) {
        warning(localize("Dwelling time is out of range."));
      }
      if (properties.dwellInSeconds) {
        writeBlock(gFormat.format(4), "P" + secFormat.format(seconds));
      } else {
        milliseconds = clamp(1, seconds * 1000, 99999999);
        writeBlock(gFormat.format(4), "P" + milliFormat.format(milliseconds));
      }
    }
    
    function onSpindleSpeed(spindleSpeed) {
      writeBlock(sOutput.format(spindleSpeed));
    }
    
    function onCycle() {
      writeBlock(gPlaneModal.format(17));
    }
    
    function getCommonCycle(x, y, z, r) {
      forceXYZ();
      return [xOutput.format(x), yOutput.format(y),
        zOutput.format(z),
        "R" + xyzFormat.format(r)];
    }
    
    function onCyclePoint(x, y, z) {
      if (isFirstCyclePoint()) {
        repositionToCycleClearance(cycle, x, y, z);
        
        // return to initial Z which is clearance plane and set absolute mode
    
        var F = cycle.feedrate;
        var P = (cycle.dwell == 0) ? 0 : cycle.dwell; // in seconds
    
        switch (cycleType) {
        case "drilling":
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(81),
            getCommonCycle(x, y, z, cycle.retract),
            feedOutput.format(F)
          );
          break;
        case "counter-boring":
          if (P > 0) {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(82),
              getCommonCycle(x, y, z, cycle.retract),
              "P" + secFormat.format(P),
              feedOutput.format(F)
            );
          } else {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(81),
              getCommonCycle(x, y, z, cycle.retract),
              feedOutput.format(F)
            );
          }
          break;
        case "chip-breaking":
          // cycle.accumulatedDepth is ignored
          if (P > 0) {
            expandCyclePoint(x, y, z);
          } else {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(73),
              getCommonCycle(x, y, z, cycle.retract),
              "Q" + xyzFormat.format(cycle.incrementalDepth),
              feedOutput.format(F)
            );
          }
          break;
        case "deep-drilling":
          if (P > 0) {
            expandCyclePoint(x, y, z);
          } else {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(83),
              getCommonCycle(x, y, z, cycle.retract),
              "Q" + xyzFormat.format(cycle.incrementalDepth),
              // conditional(P > 0, "P" + secFormat.format(P)),
              feedOutput.format(F)
            );
          }
          break;
        case "tapping":
          if (tool.type == TOOL_TAP_LEFT_HAND) {
            expandCyclePoint(x, y, z);
          } else {
            if (!F) {
              F = tool.getTappingFeedrate();
            }
            writeBlock(sOutput.format(tool.spindleRPM));
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(84),
              getCommonCycle(x, y, z, cycle.retract),
    		   "P" + secFormat.format(P),
              feedOutput.format(F)
            );
          }
          break;
        case "left-tapping":
          expandCyclePoint(x, y, z);
          break;
        case "right-tapping":
          if (!F) {
            F = tool.getTappingFeedrate();
          }
          writeBlock(mFormat.format(29), sOutput.format(tool.spindleRPM));
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(84),
            getCommonCycle(x, y, z, cycle.retract),
    		 "P" + secFormat.format(P),
            feedOutput.format(F)
          );
          break;
        case "fine-boring":
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(76),
            getCommonCycle(x, y, z, cycle.retract),
            "P" + secFormat.format(P),
            "Q" + xyzFormat.format(cycle.shift),
            feedOutput.format(F)
          );
          break;
        case "back-boring":
          var dx = (gPlaneModal.getCurrent() == 19) ? cycle.backBoreDistance : 0;
          var dy = (gPlaneModal.getCurrent() == 18) ? cycle.backBoreDistance : 0;
          var dz = (gPlaneModal.getCurrent() == 17) ? cycle.backBoreDistance : 0;
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(87),
            getCommonCycle(x - dx, y - dy, z - dz, cycle.bottom),
            "I" + xyzFormat.format(cycle.shift),
            "J" + xyzFormat.format(0),
            "P" + secFormat.format(P),
            feedOutput.format(F)
          );
          break;
        case "reaming":
          if (P > 0) {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(89),
              getCommonCycle(x, y, z, cycle.retract),
              "P" + secFormat.format(P),
              feedOutput.format(F)
            );
          } else {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(85),
              getCommonCycle(x, y, z, cycle.retract),
              feedOutput.format(F)
            );
          }
          break;
        case "stop-boring":
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(86),
            getCommonCycle(x, y, z, cycle.retract),
            "P" + secFormat.format(P),
            feedOutput.format(F)
          );
          break;
        case "manual-boring":
          writeBlock(
            gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(88),
            getCommonCycle(x, y, z, cycle.retract),
            "P" + secFormat.format(P),
            feedOutput.format(F)
          );
          break;
        case "boring":
          if (P > 0) {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(89),
              getCommonCycle(x, y, z, cycle.retract),
              "P" + secFormat.format(P),
              feedOutput.format(F)
            );
          } else {
            writeBlock(
              gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(85),
              getCommonCycle(x, y, z, cycle.retract),
              feedOutput.format(F)
            );
          }
          break;
        default:
          expandCyclePoint(x, y, z);
        }
      } 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();
      }
      
     }
    
    var pendingRadiusCompensation = -1;
    
    function onRadiusCompensation() {
      pendingRadiusCompensation = radiusCompensation;
    }
    
    function onRapid(_x, _y, _z) {
      var x = xOutput.format(_x);
      var y = yOutput.format(_y);
      var z = zOutput.format(_z);
      if (x || y || z) {
        if (pendingRadiusCompensation >= 0) {
          error(localize("Radius compensation mode cannot be changed at rapid traversal."));
          return;
        }
        writeBlock(gMotionModal.format(0), x, y, z);
        feedOutput.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;
          var d = tool.diameterOffset;
          if (d > 256) {
            warning(localize("The diameter offset exceeds the maximum value."));
          }
          writeBlock(gPlaneModal.format(17));
          switch (radiusCompensation) {
          case RADIUS_COMPENSATION_LEFT:
            dOutput.reset();
            // writeBlock(gMotionModal.format(1), dOutput.format(tool.diameter), gFormat.format(41), x, y, z, f);
            error(localize("Radius compensation mode is not supported by the CNC control."));
            break;
          case RADIUS_COMPENSATION_RIGHT:
            dOutput.reset();
            // writeBlock(gMotionModal.format(1), dOutput.format(tool.diameter), gFormat.format(42), x, y, z, f);
    		error(localize("Radius compensation mode is not supported by the CNC control."));
            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 onRapid5D(_x, _y, _z, _a, _b, _c) {
      if (pendingRadiusCompensation >= 0) {
        error(localize("Radius compensation mode cannot be changed at rapid traversal."));
        return;
      }
      var x = xOutput.format(_x);
      var y = yOutput.format(_y);
      var z = zOutput.format(_z);
      var a = aOutput.format(_a);
      var b = bOutput.format(_b);
      var c = cOutput.format(_c);
      writeBlock(gMotionModal.format(0), x, y, z, a, b, c);
      feedOutput.reset();
    }
    
    function onLinear5D(_x, _y, _z, _a, _b, _c, feed) {
      if (pendingRadiusCompensation >= 0) {
        error(localize("Radius compensation cannot be activated/deactivated for 5-axis move."));
        return;
      }
      var x = xOutput.format(_x);
      var y = yOutput.format(_y);
      var z = zOutput.format(_z);
      var a = aOutput.format(_a);
      var b = bOutput.format(_b);
      var c = cOutput.format(_c);
      var f = feedOutput.format(feed);
      if (x || y || z || a || b || c) {
        writeBlock(gMotionModal.format(1), x, y, z, a, b, c, 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;
      }
    
      var start = getCurrentPosition();
    
      if (isFullCircle()) {
        if (properties.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 - start.x, 0), jOutput.format(cy - start.y, 0), feedOutput.format(feed));
          break;
        case PLANE_ZX:
          writeBlock(gAbsIncModal.format(90), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
          break;
        case PLANE_YZ:
          writeBlock(gAbsIncModal.format(90), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
          break;
        default:
          linearize(tolerance);
        }
      } else if (!properties.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 - start.x, 0), jOutput.format(cy - start.y, 0), 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 - start.x, 0), kOutput.format(cz - start.z, 0), 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 - start.y, 0), kOutput.format(cz - start.z, 0), feedOutput.format(feed));
          break;
        default:
          linearize(tolerance);
        }
      } else { // use radius mode
        var r = getCircularRadius();
        if (toDeg(getCircularSweep()) > (180 + 1e-9)) {
          r = -r; // allow up to <360 deg arcs
        }
        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), 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), 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), feedOutput.format(feed));
          break;
        default:
          linearize(tolerance);
        }
      }
    }
    
    var mapCommand = {
      COMMAND_STOP:0,
      COMMAND_OPTIONAL_STOP:1,
      COMMAND_END:2,
      COMMAND_SPINDLE_CLOCKWISE:3,
      COMMAND_SPINDLE_COUNTERCLOCKWISE:4,
      COMMAND_STOP_SPINDLE:5,
      COMMAND_ORIENTATE_SPINDLE:19,
      COMMAND_LOAD_TOOL:6,
      COMMAND_COOLANT_ON:8, // flood
      COMMAND_COOLANT_OFF:9
    };
    
    function onCommand(command) {
      switch (command) {
      case COMMAND_START_SPINDLE:
        onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE);
        return;
      case COMMAND_LOCK_MULTI_AXIS:
        return;
      case COMMAND_UNLOCK_MULTI_AXIS:
        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() {
      writeBlock(gPlaneModal.format(17));
    
      if (((getCurrentSectionId() + 1) >= getNumberOfSections()) ||
          (tool.number != getNextSection().getTool().number)) {
        onCommand(COMMAND_BREAK_CONTROL);
      }
    
      forceAny();
      
       var initialPosition = getFramePosition(currentSection.getInitialPosition());
        writeBlock( 
    	 // zOutput.format(initialPosition.z), 
    	  mFormat.format(5), 
    	  mFormat.format(9)
    	);
    }
    
    function onClose() {
      writeln("");
    
      //onCommand(COMMAND_COOLANT_OFF);
    
    /*  if (properties.useG28) {
        writeBlock(gFormat.format(28), gAbsIncModal.format(91), "Z" + xyzFormat.format(0)); // retract
        zOutput.reset();
      }
      */
      zOutput.reset();
      setWorkPlane(new Vector(0, 0, 0)); // reset working plane
    
     /* if (properties.useG28 && !machineConfiguration.hasHomePositionX() && !machineConfiguration.hasHomePositionY()) {
        writeBlock(gFormat.format(28), gAbsIncModal.format(91), "X" + xyzFormat.format(0), "Y" + xyzFormat.format(0)); // return to home
      } else {
        var homeX;
        if (machineConfiguration.hasHomePositionX()) {
          homeX = "X" + xyzFormat.format(machineConfiguration.getHomePositionX());
        }
        var homeY;
        if (machineConfiguration.hasHomePositionY()) {
          homeY = "Y" + xyzFormat.format(machineConfiguration.getHomePositionY());
        }
        writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), homeX, homeY);
      }
    */
      onImpliedCommand(COMMAND_END);
      onImpliedCommand(COMMAND_STOP_SPINDLE);
      writeBlock(gFormat.format(30));
      writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off
      writeln("%");
    }

  3. #3
    Join Date
    Jan 2013
    Posts
    97

    Re: Latest Path Pilot Post processor for Fusion 360.

    emc.cps-Generic Enhanced Machine Controller (EMC) that comes with Fusion. Has been working great.

    I would like to change it to turn off the coolant before going to tool change position, but it isn't a big deal.

    I don't have ATC and have no idea if it would work with it.

  4. #4
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Just place an M9 where you want the coolant to stop.

  5. #5
    Join Date
    Aug 2006
    Posts
    51

    Re: Latest Path Pilot Post processor for Fusion 360.

    I use the same PP above "Tormach_pathpilotBeta_20150701" for quite some time with no problems for 3 axis work in Fusion. I have been messing around with a rotary a axis post using 3+1 CAM in fusion and am having problems with the G28 commands. Pathpilot has the ability to ignore all but the Z axis commands in a G30 but not G28. The PP I got from this web site outputs a G28. When I turn it off in fusion PP window, it outputs a G53 which PP does not seem to treat properly. Anyone have a good solution for rotary axis work with PathPiolt, Fusion 360, and a-axis posts?

  6. #6
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    I've just begun to get my feet wet with Fusion 360. Having never used CAD/CAM, I'm confused. Last night I designed a part, did all the F-360 stuff, ran the simulator, and all was fine. But where is the file? If it's in a cloud I can't use it because my machine is still here on earth. What am I missing?

  7. #7
    Join Date
    Dec 2013
    Posts
    267

    Re: Latest Path Pilot Post processor for Fusion 360.

    Quote Originally Posted by jttoner View Post
    I've just begun to get my feet wet with Fusion 360. Having never used CAD/CAM, I'm confused. Last night I designed a part, did all the F-360 stuff, ran the simulator, and all was fine. But where is the file? If it's in a cloud I can't use it because my machine is still here on earth. What am I missing?
    You'll need to wait for it to rain, then funnel that rain into your coolant tank. Make sure to cycle the coolant through your Tormach for at least 5 minutes before pressing cycle start to ensure the bits adhere to your internal memory

    ------------------

    The real answer - You need to post-process your CAM setup

    Post-processing is basically "compiling" the Fusion360 internal representation of your movement and cutting sequences to machine-specific code. Although it is "pretty standardized", G-code can be very different for different CNC controllers.

    1. Select the setup (or even specific operations) from the CAM tree
    2. Click the post-process button on the toolbar
    Attachment 313624
    3. Select your post-processor (I posted the Tormach post I use in the first page of this thread)
    4. Click the "Post" button at the bottom to save your g-code file
    5. Cut some metal!

  8. #8
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Thanks, manual coding is so much easier, but if I ever want to do complex parts, I'm going to have to learn this.

  9. #9
    Join Date
    Aug 2007
    Posts
    701

    Re: Latest Path Pilot Post processor for Fusion 360.

    Quote Originally Posted by GTOGuy View Post
    I use the same PP above "Tormach_pathpilotBeta_20150701" for quite some time with no problems for 3 axis work in Fusion. I have been messing around with a rotary a axis post using 3+1 CAM in fusion and am having problems with the G28 commands. Pathpilot has the ability to ignore all but the Z axis commands in a G30 but not G28. The PP I got from this web site outputs a G28. When I turn it off in fusion PP window, it outputs a G53 which PP does not seem to treat properly. Anyone have a good solution for rotary axis work with PathPiolt, Fusion 360, and a-axis posts?
    Essentially no rotary axis for Fusion yet. You can do 3+1 but not continuous rotary milling.

    Later this year supposedly. Hard to complain when it's free and mostly awesome though.

  10. #10
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Doing a little better, but probably not the right way. I brought up my work piece again, ran tool path, then the post that's built into F-360, ran the simulation. All looked good. The system didn't give me a file to input into a post processor, so I had to use the builtin one. I did a save as and somehow was able to find the file on my hard drive. I tried to run it in PP, got a few minor squawks that were easy to fix. PP didn't like F-360's comments, and there were a few instructions that PP didn't recognize, but in all, no show stoppers, just easy fixes. Easy considering the file was 3K+ lines of code! I ran the job (cutting air), and it seems fine. Later this week I'll go hot. It's going to take time for this old guy, but this time I'm sticking to it. After I figure out how to feed the F-360 file into a proper post I'll try something with compound curves. For me, those weren't much fun to code manually.

  11. #11
    Join Date
    Feb 2016
    Posts
    381

    Re: Latest Path Pilot Post processor for Fusion 360.

    The post .CPS files are simple to edit and personalise start by looking at a simple well commented file like the linuxCNC.cps most problems can be edited out at this point without too much effort.

  12. #12
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    The problem is I can't find the .cps files. I find the .nc files then have to copy them from the obscure default directory to my usb thing in order to load the .nc file into PP. Then I have to edit the Fusion's .nc file to make PP happy. All this is a royal PITA. Somewhere there must be a file that I can feed into my own chosen post processor. Also, I've attempted, w/o success, to change Fusion's default directory. I'm ready to throw in the towel.

  13. #13
    Join Date
    Aug 2007
    Posts
    701

    Latest Path Pilot Post processor for Fusion 360.

    I use the cloud for the cps files. If u go into your autodesk acct there's a folder called CAMposts. You will see your cloud cps files there and u can download and edit them offline and the re upload em. That's been working for me for 6 mos.

    When I post process I just have it spit the code into my Dropbox folder so PP controller can get it directly from there.

    It's in the A360:

    https://myhub.autodesk360.com/

  14. #14
    Join Date
    Feb 2016
    Posts
    381

    Re: Latest Path Pilot Post processor for Fusion 360.

    When creating the post in the post window at the top its Configuration click on setup and open folder and all the CPS files will appear in the default folder for you to browse choose or edit as required.

  15. #15
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Well, I've made some progress. I found that there is a Tormach Mach post possessor included in F-360. It seems to work well. I also learned that during Post I can specify where I want the G code to go. This works good enough for me and I can easily edit the g code as needed. I haven't figured out how to use the 4th axis yet and can't find it anywhere in F-360 tutorials. BTW, I got a lot of help from the Lars Christensen videos on U Tube. I will try Tornachs' post later but for now the F-360 post for Tormach seems OK. One more thing I haven't figured out is how to combine operations into a single program Fob example, if I face a surface then clear a pocked and finally drill a few holes, each operation has its own tool path and g code. I'm missing something important here.

  16. #16
    Join Date
    Apr 2011
    Posts
    720

    Re: Latest Path Pilot Post processor for Fusion 360.

    It sounds like you might be posting from each operation separately. If you post process from the setup line (setup1 for example) it should give you the g code for all the operations on that setup.

    Terry

  17. #17
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Terry, thanks. That should do the trick. I'm a manual guy so this is all rocket science to me. I watched a few of the F-360 tutorials but they mostly showed how good the presenter was but for me they didn't really tutor. I'm going to give it a shot right now.

  18. #18
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    Terry, it worked fine. I'm not in my shop right now, but the simulation was perfect. Face, clear, pocket, drill and chamfer. All in one fell swoop. Thanks again. I found out there's no 4th axis so when needed I'll just manually code it. All that remains is to figure out how to delete the gazillion tools in the tool table and punch in the twenty I have. Bottom line, I'm liking F-360 and the latest PP.

  19. #19
    Join Date
    Apr 2011
    Posts
    720

    Re: Latest Path Pilot Post processor for Fusion 360.

    Glad it worked out! I know what you mean about the tool table, I haven't tried to sort that out yet, still on my get around to it list. LOL
    Don't know it you are familiar with NYC CNC Machining, Fabrication & Prototype Instructional Videos - NYC CNC Prototyping and Machine Shop, but the owner does a lot of good video's on machining, and most of the mill related ones use his Tormach 1100. He recently started working with Fusion 360 most of the time and just recently did one on using the fourth axis. It's not true full-up-round fourth axis, he calls it 3 +1 I think, but it is useful to watch. I suspect you are accomplishing the same thing by just manually inserting the fourth axis moves into your code, but still a good video to watch. If you're interested just look under the Wednesday Widget tab.

    Terry

  20. #20
    Join Date
    Mar 2013
    Posts
    344

    Re: Latest Path Pilot Post processor for Fusion 360.

    While the tool table is a bit much for a hobbyist with but 20 or so tools, I do appreciate being able to specify recommended speeds and feeds for each tool as well as the vendor's name and the product number for each tool. A greater annoyance than the tool table is trying to get my 'Z' axis vertical. For some reason it defaults to a horizontal axis and it takes me a few minutes to set it to vertical. The bloody thing acts as though it were possessed. But, thanks to all who have held my hand while I try to learn F-360. I'm really beginning to like it. As for the latest iteration of Path Pilot, I recognize I did the right thing in switching from M-3 and sticking with it this time. Hopefully it will eventually support all relevant Fanuc G-codes. But it is a fine product and my hat's off to Tormach. What a great company!

Page 1 of 2 12

Similar Threads

  1. CamBam / Path Pilot Post processor
    By SomeWhatLost in forum Tormach PathPilot™
    Replies: 3
    Last Post: 01-04-2016, 05:37 PM
  2. Path pilot post processor
    By SPRSkip in forum Tormach PathPilot™
    Replies: 12
    Last Post: 11-18-2015, 04:53 PM
  3. Has anyone tested the Path Pilot Post for Fusion 360?
    By cchan_ADSK in forum Tormach PathPilot™
    Replies: 3
    Last Post: 10-29-2015, 03:22 AM
  4. CamBam Post for Path Pilot
    By phoneman in forum Tormach PathPilot™
    Replies: 3
    Last Post: 03-28-2015, 02:36 AM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •