diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index ddfcaa519b..3735fc0419 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -33,6 +33,7 @@ SET(PathScripts_SRCS
PathScripts/PathCircularHoleBaseGui.py
PathScripts/PathComment.py
PathScripts/PathCopy.py
+ PathScripts/PathCamoticsGui.py
PathScripts/PathCustom.py
PathScripts/PathCustomGui.py
PathScripts/PathDeburr.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index 309906a1b1..cc3d6340fb 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -14,6 +14,7 @@
icons/Path_BStep.svg
icons/Path_BStop.svg
icons/Path_BaseGeometry.svg
+ icons/Path_Camotics.svg
icons/Path_Comment.svg
icons/Path_Compound.svg
icons/Path_Contour.svg
@@ -130,6 +131,7 @@
panels/ToolBitSelector.ui
panels/ToolEditor.ui
panels/ToolLibraryEditor.ui
+ panels/TaskPathCamoticsSim.ui
panels/TaskPathSimulator.ui
panels/ZCorrectEdit.ui
preferences/Advanced.ui
diff --git a/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg b/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg
new file mode 100644
index 0000000000..e9d4b73037
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg
@@ -0,0 +1,644 @@
+
+
diff --git a/src/Mod/Path/Gui/Resources/icons/camotics-logo.png b/src/Mod/Path/Gui/Resources/icons/camotics-logo.png
new file mode 100644
index 0000000000..652cd11b14
Binary files /dev/null and b/src/Mod/Path/Gui/Resources/icons/camotics-logo.png differ
diff --git a/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui b/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui
new file mode 100644
index 0000000000..e73a4f179e
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui
@@ -0,0 +1,103 @@
+
+
+ TaskPathSimulator
+
+
+
+ 0
+ 0
+ 404
+ 364
+
+
+
+ Path Simulator
+
+
+ -
+
+
+ QAbstractItemView::ContiguousSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+
+ -
+
+
-
+
+
+ Estimated time to run to the selected point in the job
+
+
+ Estimated run time:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+ -
+
+
-
+
+
+ 23
+
+
+
+ -
+
+
+ Stop
+
+
+
+
+
+ -
+
+
+ Launch Camotics
+
+
+
+ :/icons/Path_Camotics.svg:/icons/Path_Camotics.svg
+
+
+
+
+
+
+
+
+
+
+ SimStop()
+ SimPlay()
+ SimPause()
+ SimStep()
+ SimFF()
+
+
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index d4011741ab..d02be4923e 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -78,6 +78,7 @@ class PathWorkbench (Workbench):
from PathScripts import PathToolBitLibraryCmd
import PathCommands
+
PathGuiInit.Startup()
# build commands list
@@ -96,8 +97,6 @@ class PathWorkbench (Workbench):
"Path_DressupLeadInOut", "Path_DressupRampEntry",
"Path_DressupTag", "Path_DressupZCorrect"]
extracmdlist = []
- # modcmdmore = ["Path_Hop",]
- # remotecmdlist = ["Path_Remote"]
specialcmdlist = []
@@ -122,6 +121,12 @@ class PathWorkbench (Workbench):
twodopcmdlist.append("Path_Slot")
if PathPreferences.advancedOCLFeaturesEnabled():
+ try:
+ import camotics
+ toolcmdlist.append("Path_Camotics")
+ except ImportError:
+ pass
+
try:
import ocl # pylint: disable=unused-variable
from PathScripts import PathSurfaceGui
diff --git a/src/Mod/Path/PathScripts/PathArray.py b/src/Mod/Path/PathScripts/PathArray.py
index e33ca1a33d..ed892318cd 100644
--- a/src/Mod/Path/PathScripts/PathArray.py
+++ b/src/Mod/Path/PathScripts/PathArray.py
@@ -24,8 +24,10 @@ import FreeCAD
import FreeCADGui
import Path
import PathScripts
+from PathScripts import PathLog
from PySide import QtCore
import math
+import random
__doc__ = """Path Array object and FreeCAD command"""
@@ -36,22 +38,28 @@ def translate(context, text, disambig=None):
class ObjectArray:
def __init__(self, obj):
- obj.addProperty("App::PropertyLink", "Base",
- "Path", "The path to array")
+ obj.addProperty("App::PropertyLinkList", "Base",
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The path(s) to array"))
obj.addProperty("App::PropertyEnumeration", "Type",
"Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Pattern method"))
obj.addProperty("App::PropertyVectorDistance", "Offset",
- "Path", "The spacing between the array copies in Linear pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The spacing between the array copies in Linear pattern"))
obj.addProperty("App::PropertyInteger", "CopiesX",
- "Path", "The number of copies in X direction in Linear pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The number of copies in X direction in Linear pattern"))
obj.addProperty("App::PropertyInteger", "CopiesY",
- "Path", "The number of copies in Y direction in Linear pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The number of copies in Y direction in Linear pattern"))
obj.addProperty("App::PropertyAngle", "Angle",
- "Path", "Total angle in Polar pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Total angle in Polar pattern"))
obj.addProperty("App::PropertyInteger", "Copies",
- "Path", "The number of copies in Linear 1D and Polar pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The number of copies in Linear 1D and Polar pattern"))
obj.addProperty("App::PropertyVector", "Centre",
- "Path", "The centre of rotation in Polar pattern")
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","The centre of rotation in Polar pattern"))
+ obj.addProperty("App::PropertyBool", "SwapDirection",
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Make copies in X direction before Y in Linear 2D pattern"))
+ obj.addProperty("App::PropertyInteger", "JitterPercent",
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Percent of copies to randomly offset"))
+ obj.addProperty("App::PropertyVectorDistance", "JitterMagnitude",
+ "Path", QtCore.QT_TRANSLATE_NOOP("App::Property","Maximum random offset of copies"))
obj.addProperty("App::PropertyLink", "ToolController",
"Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool controller that will be used to calculate the path"))
@@ -67,6 +75,9 @@ class ObjectArray:
return None
def setEditorProperties(self, obj):
+ obj.setEditorMode('JitterPercent', 0)
+ obj.setEditorMode('JitterMagnitude', 0)
+ obj.setEditorMode('ToolController', 2)
if obj.Type == 'Linear2D':
obj.setEditorMode('Angle', 2)
obj.setEditorMode('Copies', 2)
@@ -75,6 +86,7 @@ class ObjectArray:
obj.setEditorMode('CopiesX', 0)
obj.setEditorMode('CopiesY', 0)
obj.setEditorMode('Offset', 0)
+ obj.setEditorMode('SwapDirection', False)
elif obj.Type == 'Polar':
obj.setEditorMode('Angle', 0)
obj.setEditorMode('Copies', 0)
@@ -155,57 +167,99 @@ class ObjectArray:
return newPath
+ def calculateJitter(self, obj, pos):
+ if random.randint(0,100) < obj.JitterPercent:
+ pos.x = pos.x + random.uniform(-obj.JitterMagnitude.x, obj.JitterMagnitude.y)
+ pos.y = pos.y + random.uniform(-obj.JitterMagnitude.y, obj.JitterMagnitude.y)
+ pos.z = pos.z + random.uniform(-obj.JitterMagnitude.z, obj.JitterMagnitude.z)
+ return pos
+
+
def execute(self, obj):
- if obj.Base:
- if not obj.Base.isDerivedFrom("Path::Feature"):
- return
- if not obj.Base.Path:
- return
- if not obj.Base.ToolController:
- return
- obj.ToolController = obj.Base.ToolController
+ # backwards compatibility for PathArrays created before support for multiple bases
+ if isinstance(obj.Base, list):
+ base = obj.Base
+ else:
+ base = [obj.Base]
- # build copies
- basepath = obj.Base.Path
- output = ""
- if obj.Type == 'Linear1D':
- for i in range(obj.Copies):
+ if len(base)==0:
+ return
+
+ obj.ToolController = base[0].ToolController
+ for b in base:
+ if not b.isDerivedFrom("Path::Feature"):
+ return
+ if not b.Path:
+ return
+ if not b.ToolController:
+ return
+ if b.ToolController != obj.ToolController:
+ # this may be important if Job output is split by tool controller
+ PathLog.warning(QtCore.QT_TRANSLATE_NOOP("App::Property",'Arrays of paths having different tool controllers are handled according to the tool controller of the first path.'))
+
+ # build copies
+ output = ""
+ random.seed(obj.Name)
+ if obj.Type == 'Linear1D':
+ for i in range(obj.Copies):
+ pos = FreeCAD.Vector(obj.Offset.x * (i + 1), obj.Offset.y * (i + 1), 0)
+ pos = self.calculateJitter(obj, pos)
+
+ for b in base:
pl = FreeCAD.Placement()
- pos = FreeCAD.Vector(obj.Offset.x * (i + 1), obj.Offset.y * (i + 1), 0)
pl.move(pos)
np = Path.Path([cm.transform(pl)
- for cm in basepath.Commands])
+ for cm in b.Path.Commands])
output += np.toGCode()
- elif obj.Type == 'Linear2D':
+ elif obj.Type == 'Linear2D':
+ if obj.SwapDirection:
+ for i in range(obj.CopiesY + 1):
+ for j in range(obj.CopiesX + 1):
+ if (i % 2) == 0:
+ pos = FreeCAD.Vector(obj.Offset.x * j, obj.Offset.y * i, 0)
+ else:
+ pos = FreeCAD.Vector(obj.Offset.x * (obj.CopiesX - j), obj.Offset.y * i, 0)
+ pos = self.calculateJitter(obj, pos)
+
+ for b in base:
+ pl = FreeCAD.Placement()
+ # do not process the index 0,0. It will be processed by the base Paths themselves
+ if not (i == 0 and j == 0):
+ pl.move(pos)
+ np = Path.Path([cm.transform(pl) for cm in b.Path.Commands])
+ output += np.toGCode()
+ else:
for i in range(obj.CopiesX + 1):
for j in range(obj.CopiesY + 1):
- pl = FreeCAD.Placement()
- # do not process the index 0,0. It will be processed at basepath
- if not (i == 0 and j == 0):
- if (i % 2) == 0:
- pos = FreeCAD.Vector(obj.Offset.x * i, obj.Offset.y * j, 0)
- else:
- pos = FreeCAD.Vector(obj.Offset.x * i, obj.Offset.y * (obj.CopiesY - j), 0)
+ if (i % 2) == 0:
+ pos = FreeCAD.Vector(obj.Offset.x * i, obj.Offset.y * j, 0)
+ else:
+ pos = FreeCAD.Vector(obj.Offset.x * i, obj.Offset.y * (obj.CopiesY - j), 0)
+ pos = self.calculateJitter(obj, pos)
- pl.move(pos)
- np = Path.Path([cm.transform(pl)
- for cm in basepath.Commands])
- output += np.toGCode()
+ for b in base:
+ pl = FreeCAD.Placement()
+ # do not process the index 0,0. It will be processed by the base Paths themselves
+ if not (i == 0 and j == 0):
+ pl.move(pos)
+ np = Path.Path([cm.transform(pl) for cm in b.Path.Commands])
+ output += np.toGCode()
- else:
- for i in range(obj.Copies):
+ else:
+ for i in range(obj.Copies):
+ for b in base:
ang = 360
if obj.Copies > 0:
ang = obj.Angle / obj.Copies * (1 + i)
-
- np = self.rotatePath(basepath, ang, obj.Centre)
+ np = self.rotatePath(b.Path.Commands, ang, obj.Centre)
output += np.toGCode()
- # print output
- path = Path.Path(output)
- obj.Path = path
+
+ # print output
+ path = Path.Path(output)
+ obj.Path = path
class ViewProviderArray:
@@ -237,7 +291,7 @@ class CommandPathArray:
def GetResources(self):
return {'Pixmap': 'Path_Array',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Array", "Array"),
- 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Array", "Creates an array from a selected path")}
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Array", "Creates an array from selected path(s)")}
def IsActive(self):
if bool(FreeCADGui.Selection.getSelection()) is False:
@@ -252,26 +306,26 @@ class CommandPathArray:
# check that the selection contains exactly what we want
selection = FreeCADGui.Selection.getSelection()
- if len(selection) != 1:
- FreeCAD.Console.PrintError(
- translate("Path_Array", "Please select exactly one path object")+"\n")
- return
- if not(selection[0].isDerivedFrom("Path::Feature")):
- FreeCAD.Console.PrintError(
- translate("Path_Array", "Please select exactly one path object")+"\n")
- return
+
+ for sel in selection:
+ if not(sel.isDerivedFrom("Path::Feature")):
+ FreeCAD.Console.PrintError(
+ translate("Path_Array", "Arrays can be created only from Path operations.")+"\n")
+ return
# if everything is ok, execute and register the transaction in the
# undo/redo stack
FreeCAD.ActiveDocument.openTransaction("Create Array")
FreeCADGui.addModule("PathScripts.PathArray")
FreeCADGui.addModule("PathScripts.PathUtils")
- FreeCADGui.doCommand(
- 'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Array")')
+
+ FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Array")')
+
FreeCADGui.doCommand('PathScripts.PathArray.ObjectArray(obj)')
- FreeCADGui.doCommand(
- 'obj.Base = (FreeCAD.ActiveDocument.' + selection[0].Name + ')')
- # FreeCADGui.doCommand('PathScripts.PathArray.ViewProviderArray(obj.ViewObject)')
+
+ baseString = "[%s]" % ','.join(["FreeCAD.ActiveDocument.%s" % sel.Name for sel in selection])
+ FreeCADGui.doCommand('obj.Base = %s' % baseString)
+
FreeCADGui.doCommand('obj.ViewObject.Proxy = 0')
FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)')
FreeCAD.ActiveDocument.commitTransaction()
diff --git a/src/Mod/Path/PathScripts/PathCamoticsGui.py b/src/Mod/Path/PathScripts/PathCamoticsGui.py
new file mode 100644
index 0000000000..125ba47bfb
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathCamoticsGui.py
@@ -0,0 +1,400 @@
+# -*- coding: utf-8 -*-
+# ***************************************************************************
+# * Copyright (c) 2020 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import FreeCADGui
+import PathScripts.PathLog as PathLog
+# from pivy import coin
+# from itertools import cycle
+import json
+import Mesh
+import time
+import camotics
+import PathScripts.PathPost as PathPost
+import io
+import PathScripts
+import queue
+from threading import Thread, Lock
+import subprocess
+import PySide
+
+from PySide import QtCore, QtGui
+
+__title__ = "Camotics Simulator"
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "https://www.freecadweb.org"
+__doc__ = "Task panel for Camotics Simulation"
+
+PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
+PathLog.trackModule(PathLog.thisModule())
+
+# Qt translation handling
+def translate(context, text, disambig=None):
+ return QtCore.QCoreApplication.translate(context, text, disambig)
+
+
+class CAMoticsUI:
+ def __init__(self, simulation):
+ # this will create a Qt widget from our ui file
+ self.form = FreeCADGui.PySideUic.loadUi(":/panels/TaskPathCamoticsSim.ui")
+ self.simulation = simulation
+ self.opModel = PySide.QtGui.QStandardItemModel(0, 0)#len(self.columnNames()))
+ self.initializeUI()
+ self.lock = False
+
+ def columnNames(self):
+ return ['Operation']
+
+ def loadData(self):
+ self.opModel.clear()
+ self.opModel.setHorizontalHeaderLabels(self.columnNames())
+ ops = self.simulation.finalpostlist
+ for i, op in enumerate(ops):
+ libItem = PySide.QtGui.QStandardItem(op.Label)
+ libItem.setToolTip('op')
+ libItem.setData(op)
+ #libItem.setIcon(PySide.QtGui.QPixmap(':/icons/Path_ToolTable.svg'))
+ self.opModel.appendRow(libItem)
+
+
+ def initializeUI(self):
+ self.form.progressBar.reset()
+ self.updateEstimate("00:00:00")
+ self.form.btnRun.setText("Run")
+ self.form.btnLaunchCamotics.clicked.connect(self.launchCamotics)
+ self.simulation.progressUpdate.connect(self.calculating)
+ self.simulation.estimateChanged.connect(self.updateEstimate)
+ self.form.btnRun.clicked.connect(self.runButton)
+ self.form.lstPathObjects.setModel(self.opModel)
+ self.form.lstPathObjects.selectionModel().selectionChanged.connect(self.reSelect)
+ self.loadData()
+ self.selectAll()
+
+ def selectAll(self):
+ selmodel = self.form.lstPathObjects.selectionModel()
+ index0 = self.opModel.index(0, 0)
+ index1 = self.opModel.index(self.opModel.rowCount()-1,0)
+ itemSelection = QtCore.QItemSelection(index0, index1)
+ selmodel.blockSignals(True)
+ selmodel.select(itemSelection, QtCore.QItemSelectionModel.Rows | QtCore.QItemSelectionModel.Select)
+ selmodel.blockSignals(False)
+
+
+ def reSelect(self):
+ selmodel = self.form.lstPathObjects.selectionModel()
+ item = selmodel.selection().indexes()[0]
+
+ index0 = self.opModel.index(0, 0)
+ itemSelection = QtCore.QItemSelection(index0, item)
+
+ selmodel.blockSignals(True)
+ selmodel.select(itemSelection,
+ QtCore.QItemSelectionModel.Rows | QtCore.QItemSelectionModel.Select)
+ selmodel.blockSignals(False)
+
+ selectedObjs = [self.opModel.itemFromIndex(i).data() for i in selmodel.selection().indexes()]
+
+ self.simulation.setSimulationPath(selectedObjs)
+
+ def runButton(self):
+ if self.form.btnRun.text() == 'Run':
+ self.simulation.run()
+ else:
+ self.simulation.stopSim()
+
+
+ def updateEstimate(self, timestring):
+ self.form.txtRunEstimate.setText(timestring)
+
+ def launchCamotics(self):
+ projfile = self.simulation.buildproject()
+ subprocess.Popen(["camotics", projfile])
+
+ def accept(self):
+ self.simulation.accept()
+ FreeCADGui.Control.closeDialog()
+
+ def reject(self):
+ self.simulation.cancel()
+ FreeCADGui.Control.closeDialog()
+
+ def calculating(self, progress=0.0):
+ if progress < 1.0:
+ self.form.btnRun.setText('Stop')
+ else:
+ self.form.btnRun.setText('Run')
+ self.form.progressBar.setValue(int(progress*100))
+
+
+class CamoticsSimulation(QtCore.QObject):
+
+ SIM = camotics.Simulation()
+ q = queue.Queue()
+ progressUpdate = QtCore.Signal(object)
+ estimateChanged = QtCore.Signal(str)
+
+ SHAPEMAP = {'ballend': 'Ballnose',
+ 'endmill': 'Cylindrical',
+ 'v-bit' : 'Conical',
+ 'chamfer': 'Snubnose'}
+
+ cutMaterial = None
+
+ def worker(self, lock):
+ while True:
+ item = self.q.get()
+ with lock:
+ if item['TYPE'] == 'STATUS':
+ if item['VALUE'] == 'DONE':
+ self.SIM.wait()
+ surface = self.SIM.get_surface('binary')
+ #surface = self.SIM.get_surface('python')
+ self.SIM.wait()
+ self.addMesh(surface)
+ #self.makeCoinMesh(surface)
+ elif item['TYPE'] == 'PROGRESS':
+ #self.taskForm.calculating(item['VALUE'])
+ msg = item['VALUE']
+ self.progressUpdate.emit(msg)
+ self.q.task_done()
+
+
+
+ def __init__(self):
+ super().__init__() # needed for QT signals
+ lock = Lock()
+ Thread(target=self.worker, daemon=True, args=(lock,)).start()
+
+ def callback(self, status, progress):
+ self.q.put({'TYPE': 'PROGRESS', 'VALUE': progress})
+ self.q.put({'TYPE': 'STATUS' , 'VALUE': status })
+
+ def isDone(self, success):
+ self.q.put({'TYPE': 'STATUS' , 'VALUE': 'DONE'})
+
+ def stopSim(self):
+ if self.SIM.is_running():
+ self.SIM.interrupt()
+ self.SIM.wait()
+ return True
+
+
+ def addMesh(self, surface):
+ '''takes a binary stl and adds a Mesh to the current docuemnt'''
+
+ if self.cutMaterial is None:
+ self.cutMaterial = FreeCAD.ActiveDocument.addObject("Mesh::Feature", "SimulationOutput")
+ buffer=io.BytesIO()
+ buffer.write(surface)
+ buffer.seek(0)
+ mesh=Mesh.Mesh()
+ mesh.read(buffer, "STL")
+ self.cutMaterial.Mesh = mesh
+ # Mesh.show(mesh)
+
+ def setSimulationPath(self, postlist):
+ gcode, fname = PathPost.CommandPathPost().getGcodeSilently(postlist, self.job)
+ self.SIM.compute_path(gcode)
+ self.SIM.wait()
+
+ tot = sum([step['time'] for step in self.SIM.get_path()])
+ self.estimateChanged.emit(time.strftime("%H:%M:%S", time.gmtime(tot)))
+
+ def Activate(self):
+ self.job = FreeCADGui.Selection.getSelectionEx()[0].Object
+ postlist = PathPost.buildPostList(self.job)
+ self.finalpostlist = [item for slist in postlist for item in slist]
+
+ self.taskForm = CAMoticsUI(self)
+ FreeCADGui.Control.showDialog(self.taskForm)
+
+ self.SIM.set_metric()
+ self.SIM.set_resolution('high')
+
+ bb = self.job.Stock.Shape.BoundBox
+ self.SIM.set_workpiece(min = (bb.XMin, bb.YMin, bb.ZMin), max = (bb.XMax, bb.YMax, bb.ZMax))
+
+ for t in self.job.Tools.Group:
+ self.SIM.set_tool(t.ToolNumber,
+ metric = True,
+ shape = self.SHAPEMAP.get(t.Tool.ShapeName, 'Cylindrical'),
+ length = t.Tool.Length.Value,
+ diameter = t.Tool.Diameter.Value)
+
+
+ self.setSimulationPath(self.finalpostlist)
+
+ def run(self):
+ self.SIM.start(self.callback, done=self.isDone)
+
+ #def makeCoinMesh(self, surface):
+ # # this doesn't work yet
+ # #sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph();
+ # color = coin.SoBaseColor()
+ # color.rgb = (1, 0, 1)
+ # coords = coin.SoTransform()
+ # node = coin.SoSeparator()
+ # node.addChild(color)
+ # node.addChild(coords)
+
+ # end = [-1]
+ # vertices = list(zip(*[iter(surface['vertices'])] * 3))
+ # #polygons = list(zip(*[iter(vertices)] * 3, cycle(end)))
+ # polygons = list(zip(*[iter(range(len(vertices)))] * 3, cycle(end)))
+
+ # print('verts {}'.format(len(vertices)))
+ # print('polygons {}'.format(len(polygons)))
+
+ # data=coin.SoCoordinate3()
+ # face=coin.SoIndexedFaceSet()
+ # node.addChild(data)
+ # node.addChild(face)
+
+ # # i = 0
+ # # for i, v in enumerate(vertices):
+ # # print('i: {} v: {}'.format(i, v))
+ # #data.point.set1Value(i, v[0], v[1], v[2])
+ # # i += 1
+ # # i = 0
+ # # for p in polygons:
+ # # try:
+ # # face.coordIndex.set1Value(i, p)
+ # # i += 1
+ # # except Exception as e:
+ # # print(e)
+ # # print(i)
+ # # print(p)
+
+ # # sg.addChild(node)
+
+
+ def RemoveMaterial(self):
+ if self.cutMaterial is not None:
+ FreeCAD.ActiveDocument.removeObject(self.cutMaterial.Name)
+ self.cutMaterial = None
+
+ def accept(self):
+ pass
+
+ def cancel(self):
+ self.RemoveMaterial()
+
+ def buildproject(self):
+
+ job = self.job
+ gcode, fname = PathPost.CommandPathPost().getGcodeSilently(self.finalpostlist, self.job, temp=False)
+
+ tooltemplate = {
+ "units": "metric",
+ "shape": "cylindrical",
+ "length": 10,
+ "diameter": 3.125,
+ "description": ""
+ }
+
+ workpiecetemplate = {
+ "automatic": False,
+ "margin": 0,
+ "bounds": {
+ "min": [0, 0, 0],
+ "max": [0, 0, 0]}
+ }
+
+ camoticstemplate = {
+ "units": "metric",
+ "resolution-mode": "high",
+ "resolution": 1,
+ "tools": {},
+ "workpiece": {},
+ "files": []
+ }
+
+ unitstring = "imperial" if FreeCAD.Units.getSchema() in [2,3,5,7] else "metric"
+
+ camoticstemplate["units"] = unitstring
+ camoticstemplate["resolution-mode"] = "medium"
+ camoticstemplate["resolution"] = 1
+
+ toollist = {}
+ for t in job.Tools.Group:
+ tooltemplate["units"] = unitstring
+ if hasattr(t.Tool, 'Camotics'):
+ tooltemplate["shape"] = t.Tool.Camotics
+ else:
+ tooltemplate["shape"] = self.SHAPEMAP.get(t.Tool.ShapeName, 'Cylindrical')
+
+ tooltemplate["length"] = t.Tool.Length.Value
+ tooltemplate["diameter"] = t.Tool.Diameter.Value
+ tooltemplate["description"] = t.Label
+ toollist[t.ToolNumber] = tooltemplate
+
+ camoticstemplate['tools'] = toollist
+
+ bb = job.Stock.Shape.BoundBox
+
+ workpiecetemplate['bounds']['min'] = [bb.XMin, bb.YMin, bb.ZMin]
+ workpiecetemplate['bounds']['max'] = [bb.XMax, bb.YMax, bb.ZMax]
+ camoticstemplate['workpiece'] = workpiecetemplate
+
+ camoticstemplate['files'] = [fname]
+
+ foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Camotics Project File")
+ if foo:
+ tfile = foo[0]
+ else:
+ return None
+
+ with open(tfile, 'w') as t:
+ proj=json.dumps(camoticstemplate, indent=2)
+ t.write(proj)
+
+ return tfile #json.dumps(camoticstemplate, indent=2)
+
+
+class CommandCamoticsSimulate:
+ def GetResources(self):
+ return {'Pixmap': 'Path_Camotics',
+ 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Camotics", "Camotics"),
+ 'Accel': "P, C",
+ 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Camotics", "Simulate using Camotics"),
+ 'CmdType': "ForEdit"}
+
+ def IsActive(self):
+ if bool(FreeCADGui.Selection.getSelection()) is False:
+ return False
+ try:
+ job = FreeCADGui.Selection.getSelectionEx()[0].Object
+ return isinstance(job.Proxy, PathScripts.PathJob.ObjectJob)
+ except:
+ return False
+
+ def Activated(self):
+ pathSimulation.Activate()
+
+
+pathSimulation = CamoticsSimulation()
+
+if FreeCAD.GuiUp:
+ FreeCADGui.addCommand('Path_Camotics', CommandCamoticsSimulate())
+
+
+FreeCAD.Console.PrintLog("Loading PathCamoticsSimulateGui ... done\n")
diff --git a/src/Mod/Path/PathScripts/PathGuiInit.py b/src/Mod/Path/PathScripts/PathGuiInit.py
index 405dcb4fcc..d7afd21f49 100644
--- a/src/Mod/Path/PathScripts/PathGuiInit.py
+++ b/src/Mod/Path/PathScripts/PathGuiInit.py
@@ -39,17 +39,23 @@ def Startup():
PathLog.debug('Initializing PathGui')
from PathScripts import PathAdaptiveGui
from PathScripts import PathArray
+ try:
+ import camotics
+ except ImportError:
+ import FreeCAD
+ FreeCAD.Console.PrintError("Camotics is not available.\n")
+ else:
+ from PathScripts import PathCamoticsGui
from PathScripts import PathComment
- # from PathScripts import PathCustom
from PathScripts import PathCustomGui
from PathScripts import PathDeburrGui
from PathScripts import PathDressupAxisMap
from PathScripts import PathDressupDogbone
from PathScripts import PathDressupDragknife
- from PathScripts import PathDressupRampEntry
- from PathScripts import PathDressupPathBoundaryGui
- from PathScripts import PathDressupTagGui
from PathScripts import PathDressupLeadInOut
+ from PathScripts import PathDressupPathBoundaryGui
+ from PathScripts import PathDressupRampEntry
+ from PathScripts import PathDressupTagGui
from PathScripts import PathDressupZCorrect
from PathScripts import PathDrillingGui
from PathScripts import PathEngraveGui
@@ -62,9 +68,6 @@ def Startup():
from PathScripts import PathPocketShapeGui
from PathScripts import PathPost
from PathScripts import PathProbeGui
- # from PathScripts import PathProfileContourGui
- # from PathScripts import PathProfileEdgesGui
- # from PathScripts import PathProfileFacesGui
from PathScripts import PathProfileGui
from PathScripts import PathPropertyBagGui
from PathScripts import PathSanity
@@ -73,14 +76,12 @@ def Startup():
from PathScripts import PathSimulatorGui
from PathScripts import PathSlotGui
from PathScripts import PathStop
- # from PathScripts import PathSurfaceGui # Added in initGui.py due to OCL dependency
from PathScripts import PathThreadMillingGui
from PathScripts import PathToolController
from PathScripts import PathToolControllerGui
- from PathScripts import PathToolLibraryManager
from PathScripts import PathToolLibraryEditor
+ from PathScripts import PathToolLibraryManager
from PathScripts import PathUtilsGui
- # from PathScripts import PathWaterlineGui # Added in initGui.py due to OCL dependency
from PathScripts import PathVcarveGui
Processed = True
else:
diff --git a/src/Mod/Path/PathScripts/PathPost.py b/src/Mod/Path/PathScripts/PathPost.py
index fec6ddd3a5..1a0699b801 100644
--- a/src/Mod/Path/PathScripts/PathPost.py
+++ b/src/Mod/Path/PathScripts/PathPost.py
@@ -33,6 +33,7 @@ import PathScripts.PathPreferences as PathPreferences
import PathScripts.PathUtil as PathUtil
import PathScripts.PathUtils as PathUtils
import os
+import tempfile
from PathScripts.PathPostProcessor import PostProcessor
from PySide import QtCore, QtGui
@@ -56,6 +57,187 @@ class _TempObject:
Label = "Fixture"
+def resolveFileName(job):
+
+ path = PathPreferences.defaultOutputFile()
+ if job.PostProcessorOutputFile:
+ path = job.PostProcessorOutputFile
+ filename = path
+
+ if '%D' in filename:
+ D = FreeCAD.ActiveDocument.FileName
+ if D:
+ D = os.path.dirname(D)
+ # in case the document is in the current working directory
+ if not D:
+ D = '.'
+ else:
+ FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
+ return None
+ filename = filename.replace('%D', D)
+
+ if '%d' in filename:
+ d = FreeCAD.ActiveDocument.Label
+ filename = filename.replace('%d', d)
+
+ if '%j' in filename:
+ j = job.Label
+ filename = filename.replace('%j', j)
+
+ if '%M' in filename:
+ pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
+ M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
+ filename = filename.replace('%M', M)
+
+
+ policy = PathPreferences.defaultOutputPolicy()
+
+ openDialog = policy == 'Open File Dialog'
+ if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
+ # Either the entire filename resolves into a directory or the parent directory doesn't exist.
+ # Either way I don't know what to do - ask for help
+ openDialog = True
+
+ if os.path.isfile(filename) and not openDialog:
+ if policy == 'Open File Dialog on conflict':
+ openDialog = True
+ elif policy == 'Append Unique ID on conflict':
+ fn, ext = os.path.splitext(filename)
+ nr = fn[-3:]
+ n = 1
+ if nr.isdigit():
+ n = int(nr)
+ while os.path.isfile("%s%03d%s" % (fn, n, ext)):
+ n = n + 1
+ filename = "%s%03d%s" % (fn, n, ext)
+
+ if openDialog:
+ foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
+ if foo[0]:
+ filename = foo[0]
+ else:
+ filename = None
+
+ return filename
+
+def buildPostList(job):
+ ''' Takes the job and determines the specific objects and order to
+ postprocess Returns a list of objects which can be passed to
+ exportObjectsWith() for final posting'''
+ wcslist = job.Fixtures
+ orderby = job.OrderOutputBy
+
+ postlist = []
+
+ if orderby == 'Fixture':
+ PathLog.debug("Ordering by Fixture")
+ # Order by fixture means all operations and tool changes will be completed in one
+ # fixture before moving to the next.
+
+ currTool = None
+ for index, f in enumerate(wcslist):
+ # create an object to serve as the fixture path
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ fobj.Path = Path.Path([c1])
+ if index != 0:
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path.addCommands(c2)
+ fobj.InList.append(job)
+ sublist = [fobj]
+
+ # Now generate the gcode
+ for obj in job.Operations.Group:
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is not None and PathUtil.opProperty(obj, 'Active'):
+ if tc.ToolNumber != currTool:
+ sublist.append(tc)
+ PathLog.debug("Appending TC: {}".format(tc.Name))
+ currTool = tc.ToolNumber
+ sublist.append(obj)
+ postlist.append(sublist)
+
+ elif orderby == 'Tool':
+ PathLog.debug("Ordering by Tool")
+ # Order by tool means tool changes are minimized.
+ # all operations with the current tool are processed in the current
+ # fixture before moving to the next fixture.
+
+ currTool = None
+ fixturelist = []
+ for f in wcslist:
+ # create an object to serve as the fixture path
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path = Path.Path([c1, c2])
+ fobj.InList.append(job)
+ fixturelist.append(fobj)
+
+ # Now generate the gcode
+ curlist = [] # list of ops for tool, will repeat for each fixture
+ sublist = [] # list of ops for output splitting
+
+ for idx, obj in enumerate(job.Operations.Group):
+
+ # check if the operation is active
+ active = PathUtil.opProperty(obj, 'Active')
+
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is None or tc.ToolNumber == currTool and active:
+ curlist.append(obj)
+ elif tc.ToolNumber != currTool and currTool is None and active: # first TC
+ sublist.append(tc)
+ curlist.append(obj)
+ currTool = tc.ToolNumber
+ elif tc.ToolNumber != currTool and currTool is not None and active: # TC
+ for fixture in fixturelist:
+ sublist.append(fixture)
+ sublist.extend(curlist)
+ postlist.append(sublist)
+ sublist = [tc]
+ curlist = [obj]
+ currTool = tc.ToolNumber
+
+ if idx == len(job.Operations.Group) - 1: # Last operation.
+ for fixture in fixturelist:
+ sublist.append(fixture)
+ sublist.extend(curlist)
+ postlist.append(sublist)
+
+ elif orderby == 'Operation':
+ PathLog.debug("Ordering by Operation")
+ # Order by operation means ops are done in each fixture in
+ # sequence.
+ currTool = None
+ firstFixture = True
+
+ # Now generate the gcode
+ for obj in job.Operations.Group:
+ if PathUtil.opProperty(obj, 'Active'):
+ sublist = []
+ PathLog.debug("obj: {}".format(obj.Name))
+ for f in wcslist:
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ fobj.Path = Path.Path([c1])
+ if not firstFixture:
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path.addCommands(c2)
+ fobj.InList.append(job)
+ sublist.append(fobj)
+ firstFixture = False
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is not None:
+ if job.SplitOutput or (tc.ToolNumber != currTool):
+ sublist.append(tc)
+ currTool = tc.ToolNumber
+ sublist.append(obj)
+ postlist.append(sublist)
+
+ return postlist
+
+
class DlgSelectPostProcessor:
def __init__(self, parent=None):
@@ -97,73 +279,6 @@ class CommandPathPost:
# pylint: disable=no-init
subpart = 1
- def resolveFileName(self, job):
- path = PathPreferences.defaultOutputFile()
- if job.PostProcessorOutputFile:
- path = job.PostProcessorOutputFile
- filename = path
-
- if '%D' in filename:
- D = FreeCAD.ActiveDocument.FileName
- if D:
- D = os.path.dirname(D)
- # in case the document is in the current working directory
- if not D:
- D = '.'
- else:
- FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
- return None
- filename = filename.replace('%D', D)
-
- if '%d' in filename:
- d = FreeCAD.ActiveDocument.Label
- filename = filename.replace('%d', d)
-
- if '%j' in filename:
- j = job.Label
- filename = filename.replace('%j', j)
-
- if '%M' in filename:
- pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
- M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
- filename = filename.replace('%M', M)
-
- if '%s' in filename:
- if job.SplitOutput:
- filename = filename.replace('%s', '_'+str(self.subpart))
- self.subpart += 1
- else:
- filename = filename.replace('%s', '')
-
- policy = PathPreferences.defaultOutputPolicy()
-
- openDialog = policy == 'Open File Dialog'
- if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
- # Either the entire filename resolves into a directory or the parent directory doesn't exist.
- # Either way I don't know what to do - ask for help
- openDialog = True
-
- if os.path.isfile(filename) and not openDialog:
- if policy == 'Open File Dialog on conflict':
- openDialog = True
- elif policy == 'Append Unique ID on conflict':
- fn, ext = os.path.splitext(filename)
- nr = fn[-3:]
- n = 1
- if nr.isdigit():
- n = int(nr)
- while os.path.isfile("%s%03d%s" % (fn, n, ext)):
- n = n + 1
- filename = "%s%03d%s" % (fn, n, ext)
-
- if openDialog:
- foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
- if foo:
- filename = foo[0]
- else:
- filename = None
-
- return filename
def resolvePostProcessor(self, job):
if hasattr(job, "PostProcessor"):
@@ -190,6 +305,37 @@ class CommandPathPost:
return False
+ def getGcodeSilently(self, objs, job, temp=True):
+ '''This method will postprocess the given objects without opening dialogs
+ or prompting the user for any input. gcode is returned as a list of lists
+ suitable for splitting'''
+ PathLog.track('objs: {}'.format(objs))
+
+ postArgs = PathPreferences.defaultPostProcessorArgs()
+ if hasattr(job, "PostProcessorArgs") and job.PostProcessorArgs:
+ postArgs = job.PostProcessorArgs
+ elif hasattr(job, "PostProcessor") and job.PostProcessor:
+ postArgs = ''
+
+ if not '--no-show-editor' in postArgs:
+ postArgs += ' --no-show-editor'
+
+ PathLog.track('postArgs: {}'.format(postArgs))
+
+ if temp:
+ tfile = tempfile.NamedTemporaryFile()
+ filename = tfile.name
+ else:
+ filename = resolveFileName(job)
+ tfile = open(filename, "w")
+
+ with tfile:
+ postname = self.resolvePostProcessor(job)
+ processor = PostProcessor.load(postname)
+ gcode = processor.export(objs, filename, postArgs)
+ return gcode, filename
+
+
def exportObjectsWith(self, objs, job, needFilename=True):
PathLog.track()
# check if the user has a project and has set the default post and
@@ -203,7 +349,7 @@ class CommandPathPost:
postname = self.resolvePostProcessor(job)
filename = '-'
if postname and needFilename:
- filename = self.resolveFileName(job)
+ filename = resolveFileName(job)
if postname and filename:
print("post: %s(%s, %s)" % (postname, filename, postArgs))
@@ -261,123 +407,17 @@ class CommandPathPost:
PathLog.debug("about to postprocess job: {}".format(job.Name))
- wcslist = job.Fixtures
- orderby = job.OrderOutputBy
- split = job.SplitOutput
+ postlist = buildPostList(job)
+ filename = resolveFileName(job)
- postlist = []
-
- if orderby == 'Fixture':
- PathLog.debug("Ordering by Fixture")
- # Order by fixture means all operations and tool changes will be completed in one
- # fixture before moving to the next.
-
- currTool = None
- for index, f in enumerate(wcslist):
- # create an object to serve as the fixture path
- fobj = _TempObject()
- c1 = Path.Command(f)
- fobj.Path = Path.Path([c1])
- if index != 0:
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path.addCommands(c2)
- fobj.InList.append(job)
- sublist = [fobj]
-
- # Now generate the gcode
- for obj in job.Operations.Group:
- tc = PathUtil.toolControllerForOp(obj)
- if tc is not None and PathUtil.opProperty(obj, 'Active'):
- if tc.ToolNumber != currTool:
- sublist.append(tc)
- PathLog.debug("Appending TC: {}".format(tc.Name))
- currTool = tc.ToolNumber
- sublist.append(obj)
- postlist.append(sublist)
-
- elif orderby == 'Tool':
- PathLog.debug("Ordering by Tool")
- # Order by tool means tool changes are minimized.
- # all operations with the current tool are processed in the current
- # fixture before moving to the next fixture.
-
- currTool = None
- fixturelist = []
- for f in wcslist:
- # create an object to serve as the fixture path
- fobj = _TempObject()
- c1 = Path.Command(f)
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path = Path.Path([c1, c2])
- fobj.InList.append(job)
- fixturelist.append(fobj)
-
- # Now generate the gcode
- curlist = [] # list of ops for tool, will repeat for each fixture
- sublist = [] # list of ops for output splitting
-
- for idx, obj in enumerate(job.Operations.Group):
-
- # check if the operation is active
- active = PathUtil.opProperty(obj, 'Active')
-
- tc = PathUtil.toolControllerForOp(obj)
- if tc is None or tc.ToolNumber == currTool and active:
- curlist.append(obj)
- elif tc.ToolNumber != currTool and currTool is None and active: # first TC
- sublist.append(tc)
- curlist.append(obj)
- currTool = tc.ToolNumber
- elif tc.ToolNumber != currTool and currTool is not None and active: # TC
- for fixture in fixturelist:
- sublist.append(fixture)
- sublist.extend(curlist)
- postlist.append(sublist)
- sublist = [tc]
- curlist = [obj]
- currTool = tc.ToolNumber
-
- if idx == len(job.Operations.Group) - 1: # Last operation.
- for fixture in fixturelist:
- sublist.append(fixture)
- sublist.extend(curlist)
- postlist.append(sublist)
-
- elif orderby == 'Operation':
- PathLog.debug("Ordering by Operation")
- # Order by operation means ops are done in each fixture in
- # sequence.
- currTool = None
- firstFixture = True
-
- # Now generate the gcode
- for obj in job.Operations.Group:
- if PathUtil.opProperty(obj, 'Active'):
- sublist = []
- PathLog.debug("obj: {}".format(obj.Name))
- for f in wcslist:
- fobj = _TempObject()
- c1 = Path.Command(f)
- fobj.Path = Path.Path([c1])
- if not firstFixture:
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path.addCommands(c2)
- fobj.InList.append(job)
- sublist.append(fobj)
- firstFixture = False
- tc = PathUtil.toolControllerForOp(obj)
- if tc is not None:
- if job.SplitOutput or (tc.ToolNumber != currTool):
- sublist.append(tc)
- currTool = tc.ToolNumber
- sublist.append(obj)
- postlist.append(sublist)
fail = True
rc = ''
if split:
for slist in postlist:
(fail, rc, filename) = self.exportObjectsWith(slist, job)
+ if fail:
+ break
else:
finalpostlist = [item for slist in postlist for item in slist]
(fail, rc, filename) = self.exportObjectsWith(finalpostlist, job)
diff --git a/src/Mod/Path/PathScripts/post/fanuc_post.py b/src/Mod/Path/PathScripts/post/fanuc_post.py
index d2bb6acb0e..3eee41519d 100644
--- a/src/Mod/Path/PathScripts/post/fanuc_post.py
+++ b/src/Mod/Path/PathScripts/post/fanuc_post.py
@@ -30,7 +30,6 @@ import datetime
import shlex
import os.path
from PathScripts import PostUtils
-from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -154,7 +153,7 @@ def processArguments(argstring):
if args.no_tlo:
USE_TLO = False
if args.no_axis_modal:
- OUTPUT_DOUBLES = true
+ OUTPUT_DOUBLES = True
except Exception: # pylint: disable=broad-except
return False
@@ -171,7 +170,7 @@ def export(objectslist, filename, argstring):
global UNIT_SPEED_FORMAT
global HORIZRAPID
global VERTRAPID
-
+
for obj in objectslist:
if not hasattr(obj, "Path"):
print("the object " + obj.Name + " is not a path. Please select only path and Compounds.")
@@ -206,34 +205,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
- # fetch machine details
- job = PathUtils.findParentJob(obj)
-
- myMachine = 'not set'
-
- if hasattr(job, "MachineName"):
- myMachine = job.MachineName
-
- if hasattr(job, "MachineUnits"):
- if job.MachineUnits == "Metric":
- UNITS = "G21"
- UNIT_FORMAT = 'mm'
- UNIT_SPEED_FORMAT = 'mm/min'
- else:
- UNITS = "G20"
- UNIT_FORMAT = 'in'
- UNIT_SPEED_FORMAT = 'in/min'
-
- if hasattr(job, "SetupSheet"):
- if hasattr(job.SetupSheet, "HorizRapid"):
- HORIZRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
- if hasattr(job.SetupSheet, "VertRapid"):
- VERTRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
-
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(BEGIN OPERATION: %s)\n" % obj.Label.upper()
- gcode += linenumber() + "(MACHINE: %s, %s)\n" % (myMachine.upper(), UNIT_SPEED_FORMAT.upper())
+ gcode += linenumber() + "(MACHINE UNITS: %s)\n" % (UNIT_SPEED_FORMAT.upper())
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
@@ -459,7 +434,6 @@ def parse(pathobj):
outstring.append(param + str(int(c.Parameters['D'])))
elif param == 'S':
outstring.append(param + str(int(c.Parameters['S'])))
- currentSpeed = int(c.Parameters['S'])
else:
if (not OUTPUT_DOUBLES) and (param in currLocation) and (currLocation[param] == c.Parameters[param]):
continue
@@ -482,7 +456,6 @@ def parse(pathobj):
# Check for Tool Change:
if command == 'M6':
# stop the spindle
- currentSpeed = 0
out += linenumber() + "M5\n"
for line in TOOL_CHANGE.splitlines(True):
out += linenumber() + line
diff --git a/src/Mod/Path/PathScripts/post/heidenhain_post.py b/src/Mod/Path/PathScripts/post/heidenhain_post.py
index 42219952b0..acfc1a9b22 100644
--- a/src/Mod/Path/PathScripts/post/heidenhain_post.py
+++ b/src/Mod/Path/PathScripts/post/heidenhain_post.py
@@ -20,14 +20,10 @@
# HEDENHAIN Post-Processor for FreeCAD
-import FreeCAD
-from FreeCAD import Units
import argparse
-import time
import shlex
import PathScripts
from PathScripts import PostUtils
-from PathScripts import PathUtils
import math
#**************************************************************************#
@@ -261,11 +257,8 @@ def export(objectslist, filename, argstring):
global LBLIZE_STAUS
Object_Kind = None
- Feed_Rapid = False
Feed = 0
- Spindle_RPM = 0
Spindle_Active = False
- ToolId = ""
Compensation = "0"
params = [
'X', 'Y', 'Z',
@@ -360,22 +353,14 @@ def export(objectslist, filename, argstring):
for c in obj.Path.Commands:
Cmd_Count += 1
- outstring = []
command = c.Name
if command != 'G0':
command = command.replace('G0','G') # normalize: G01 -> G1
- Feed_Rapid = False
- else:
- Feed_Rapid = True
for param in params:
if param in c.Parameters:
if param == 'F':
Feed = c.Parameters['F']
- elif param == 'S':
- Spindle_RPM = c.Parameters['S'] # Could be deleted if tool it's OK
- elif param == 'T':
- ToolId = c.Parameters['T'] # Could be deleted if tool it's OK
if command == 'G90':
G_FUNCTION_STORE['G90'] = True
@@ -474,21 +459,21 @@ def export(objectslist, filename, argstring):
def HEIDEN_Begin(ActualJob): #use Label for program name
global UNITS
- JobParent = PathUtils.findParentJob(ActualJob[0])
- if hasattr(JobParent, "Label"):
- program_id = JobParent.Label
- else:
- program_id = "NEW"
- return "BEGIN PGM " + str(program_id) + " " + UNITS
+ # JobParent = PathUtils.findParentJob(ActualJob[0])
+ # if hasattr(JobParent, "Label"):
+ # program_id = JobParent.Label
+ # else:
+ # program_id = "NEW"
+ return "BEGIN PGM {}".format(UNITS)
def HEIDEN_End(ActualJob): #use Label for program name
global UNITS
- JobParent = PathUtils.findParentJob(ActualJob[0])
- if hasattr(JobParent, "Label"):
- program_id = JobParent.Label
- else:
- program_id = "NEW"
- return "END PGM " + program_id + " " + UNITS
+ # JobParent = PathUtils.findParentJob(ActualJob[0])
+ # if hasattr(JobParent, "Label"):
+ # program_id = JobParent.Label
+ # else:
+ # program_id = "NEW"
+ return "END PGM {}".format(UNITS)
#def HEIDEN_ToolDef(tool_id, tool_length, tool_radius): # old machines don't have tool table, need tooldef list
# return "TOOL DEF " + tool_id + " R" + "{:.3f}".format(tool_length) + " L" + "{:.3f}".format(tool_radius)
diff --git a/src/Mod/Path/PathScripts/post/linuxcnc_post.py b/src/Mod/Path/PathScripts/post/linuxcnc_post.py
index 6f22d93142..c102528d6b 100644
--- a/src/Mod/Path/PathScripts/post/linuxcnc_post.py
+++ b/src/Mod/Path/PathScripts/post/linuxcnc_post.py
@@ -29,7 +29,6 @@ import argparse
import datetime
import shlex
from PathScripts import PostUtils
-from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -193,28 +192,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
- # fetch machine details
- job = PathUtils.findParentJob(obj)
-
- myMachine = 'not set'
-
- if hasattr(job, "MachineName"):
- myMachine = job.MachineName
-
- if hasattr(job, "MachineUnits"):
- if job.MachineUnits == "Metric":
- UNITS = "G21"
- UNIT_FORMAT = 'mm'
- UNIT_SPEED_FORMAT = 'mm/min'
- else:
- UNITS = "G20"
- UNIT_FORMAT = 'in'
- UNIT_SPEED_FORMAT = 'in/min'
-
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
- gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT)
+ gcode += linenumber() + "(machine units: %s)\n" % (UNIT_SPEED_FORMAT)
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
diff --git a/src/Mod/Path/PathScripts/post/mach3_mach4_post.py b/src/Mod/Path/PathScripts/post/mach3_mach4_post.py
index f2ca58259d..b34f8cae81 100644
--- a/src/Mod/Path/PathScripts/post/mach3_mach4_post.py
+++ b/src/Mod/Path/PathScripts/post/mach3_mach4_post.py
@@ -28,7 +28,6 @@ import argparse
import datetime
import shlex
from PathScripts import PostUtils
-from PathScripts import PathUtils
TOOLTIP = '''
This is a postprocessor file for the Path workbench. It is used to
@@ -161,8 +160,6 @@ def export(objectslist, filename, argstring):
global UNITS
global UNIT_FORMAT
global UNIT_SPEED_FORMAT
- global HORIZRAPID
- global VERTRAPID
for obj in objectslist:
if not hasattr(obj, "Path"):
@@ -195,34 +192,10 @@ def export(objectslist, filename, argstring):
if not obj.Base.Active:
continue
- # fetch machine details
- job = PathUtils.findParentJob(obj)
-
- myMachine = 'not set'
-
- if hasattr(job, "MachineName"):
- myMachine = job.MachineName
-
- if hasattr(job, "MachineUnits"):
- if job.MachineUnits == "Metric":
- UNITS = "G21"
- UNIT_FORMAT = 'mm'
- UNIT_SPEED_FORMAT = 'mm/min'
- else:
- UNITS = "G20"
- UNIT_FORMAT = 'in'
- UNIT_SPEED_FORMAT = 'in/min'
-
- if hasattr(job, "SetupSheet"):
- if hasattr(job.SetupSheet, "HorizRapid"):
- HORIZRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
- if hasattr(job.SetupSheet, "VertRapid"):
- VERTRAPID = Units.Quantity(job.SetupSheet.HorizRapid, FreeCAD.Units.Velocity)
-
# do the pre_op
if OUTPUT_COMMENTS:
gcode += linenumber() + "(begin operation: %s)\n" % obj.Label
- gcode += linenumber() + "(machine: %s, %s)\n" % (myMachine, UNIT_SPEED_FORMAT)
+ gcode += linenumber() + "(machine: %s, %s)\n" % (MACHINE_NAME, UNIT_SPEED_FORMAT)
for line in PRE_OPERATION.splitlines(True):
gcode += linenumber() + line
diff --git a/src/Mod/Path/PathScripts/post/philips_post.py b/src/Mod/Path/PathScripts/post/philips_post.py
index f06d2d666d..d48a5265c9 100644
--- a/src/Mod/Path/PathScripts/post/philips_post.py
+++ b/src/Mod/Path/PathScripts/post/philips_post.py
@@ -20,15 +20,14 @@
#* *
#***************************************************************************
-# reload in python console:
-# import generic_post
-# reload(generic_post)
+# 03-24-2021 Sliptonic: I've removed teh PathUtils import and job lookup
+# post processors shouldn't be reaching back to the job. This can cause a
+# proxy error.
import FreeCAD
import argparse
import time
from PathScripts import PostUtils
-from PathScripts import PathUtils
import math
TOOLTIP = '''Post processor for Maho M 600E mill
@@ -232,16 +231,17 @@ def processArguments(argstring):
SHOW_EDITOR = False
def mkHeader(selection):
- job = PathUtils.findParentJob(selection[0])
+ # job = PathUtils.findParentJob(selection[0])
# this is within a function, because otherwise filename and time don't change when changing the FreeCAD project
# now = datetime.datetime.now()
now = time.strftime("%Y-%m-%d %H:%M")
originfile = FreeCAD.ActiveDocument.FileName
headerNoNumber = "%PM\n" # this line gets no linenumber
- if hasattr(job, "Description"):
- description = job.Description
- else:
- description = ""
+ # if hasattr(job, "Description"):
+ # description = job.Description
+ # else:
+ # description = ""
+ description = ""
# this line gets no linenumber, it is already a specially numbered
headerNoNumber += "N9XXX (" + description + ", " + now + ")\n"
header = ""