Merge pull request #6726 from sliptonic/bug/2TCspinspeed

[Path] Fixes #6275  Output for multiple TCs with same ToolNumber
This commit is contained in:
sliptonic
2022-04-18 09:24:32 -05:00
committed by GitHub
6 changed files with 540 additions and 145 deletions

View File

@@ -75,3 +75,33 @@ def generate(
PathLog.track(commands)
return commands
def generateSubstitute(newTC, oldTC=None):
"""
The specific commands to emit, depend on the state of the machine.
For example, the toolnumber may not change, only the spindle speed.
This routine will generate a list of commands to substitute for a TC
object to be handed to the postprocessor.
It will contain only the commands needed
"""
if oldTC is None:
return newTC.Path.Commands
if newTC.ToolNumber != oldTC.ToolNumber: # Full toolchange
return newTC.Path.Commands
if (newTC.SpindleSpeed != oldTC.SpindleSpeed) or (
newTC.SpindleDir != oldTC.SpindleDir
):
if newTC.SpindleDir == "Forward":
sd = SpindleDirection.CW
elif newTC.SpindleDir == "Reverse":
sd = SpindleDirection.CCW
else:
sd = SpindleDirection.OFF
return [Path.Command(sd.value, {"S": newTC.SpindleSpeed})]
return []

View File

@@ -21,9 +21,11 @@
# ***************************************************************************
import FreeCAD
import FreeCADGui
import PathScripts.PathGeom as PathGeom
import PathScripts.PathLog as PathLog
import PathScripts.PathUtil as PathUtil
from PySide import QtGui, QtCore
from PySide import QtCore, QtGui
@@ -199,3 +201,38 @@ class QuantitySpinBox(QtCore.QObject):
if prop == self.prop:
return exp
return None
def getDocNode():
doc = FreeCADGui.ActiveDocument.Document.Name
tws = FreeCADGui.getMainWindow().findChildren(QtGui.QTreeWidget)
for tw in tws:
if tw.topLevelItemCount() != 1 or tw.topLevelItem(0).text(0) != "Application":
continue
toptree = tw.topLevelItem(0)
for i in range(0, toptree.childCount()):
docitem = toptree.child(i)
if docitem.text(0) == doc:
return docitem
return None
def disableItem(item):
Dragflag = QtCore.Qt.ItemFlag.ItemIsDragEnabled
Dropflag = QtCore.Qt.ItemFlag.ItemIsDropEnabled
item.setFlags(item.flags() & ~Dragflag)
item.setFlags(item.flags() & ~Dropflag)
for idx in range(0, item.childCount()):
disableItem(item.child(idx))
def findItem(docitem, objname):
print(docitem.text(0))
for i in range(0, docitem.childCount()):
if docitem.child(i).text(0) == objname:
return docitem.child(i)
res = findItem(docitem.child(i), objname)
if res:
return res
return None

View File

@@ -41,8 +41,11 @@ from PySide.QtCore import QT_TRANSLATE_NOOP
LOG_MODULE = PathLog.thisModule()
PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
if False:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
translate = FreeCAD.Qt.translate
@@ -50,7 +53,6 @@ translate = FreeCAD.Qt.translate
class _TempObject:
Path = None
Name = "Fixture"
InList = []
Label = "Fixture"
@@ -216,6 +218,167 @@ class CommandPathPost:
else:
return (True, "", filename)
def buildPostList(self, job):
"""
Parses the job and returns the list(s) of objects to be written by the post
Postlist is a list of lists. Each sublist is intended to be a separate file
"""
orderby = job.OrderOutputBy
fixturelist = []
for f in job.Fixtures:
# create an object to serve as the fixture path
fobj = _TempObject()
fobj.Label = f
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)
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.
for f in fixturelist:
scratchpad = [(f, None)]
# Now generate the gcode
for obj in job.Operations.Group:
if not PathUtil.opProperty(obj, "Active"):
continue
tc = PathUtil.toolControllerForOp(obj)
scratchpad.append((obj, tc))
sublist = []
temptool = None
for item in scratchpad:
if item[1] in [temptool, None]:
sublist.append(item[0])
else:
sublist.append(item[1])
temptool = item[1]
sublist.append(item[0])
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
# 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 not active: # pass on any inactive ops
continue
if tc is None:
curlist.append((obj, None))
continue
if tc == currTool:
curlist.append((obj, tc))
if tc != currTool and currTool is None: # first TC
currTool = tc
curlist.append((obj, tc))
continue
if tc != currTool and currTool is not None: # TC changed
if tc.ToolNumber == currTool.ToolNumber: # Same tool /diff params
curlist.append((obj, tc))
currTool = tc
else: # Actual Toolchange
# dump current state to postlist
sublist = []
t = None
for fixture in fixturelist:
sublist.append(fixture)
for item in curlist:
if item[1] == t:
sublist.append(item[0])
else:
sublist.append(item[1])
t = item[1]
sublist.append(item[0])
postlist.append(sublist)
# set up for next tool group
currTool = tc
curlist = [(obj, tc)]
# flush remaining curlist to output
sublist = []
t = None
for fixture in fixturelist:
sublist.append(fixture)
for item in curlist:
if item[1] == t:
sublist.append(item[0])
else:
sublist.append(item[1])
t = item[1]
sublist.append(item[0])
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:
scratchpad = []
tc = PathUtil.toolControllerForOp(obj)
if not PathUtil.opProperty(obj, "Active"):
continue
PathLog.debug("obj: {}".format(obj.Name))
for f in fixturelist:
scratchpad.append((f, None))
scratchpad.append((obj, tc))
sublist = []
temptool = None
for item in scratchpad:
if item[1] in [temptool, None]:
sublist.append(item[0])
else:
sublist.append(item[1])
temptool = item[1]
sublist.append(item[0])
postlist.append(sublist)
if job.SplitOutput:
return postlist
else:
finalpostlist = [item for slist in postlist for item in slist]
return [finalpostlist]
def Activated(self):
PathLog.track()
FreeCAD.ActiveDocument.openTransaction("Post Process the Selected path(s)")
@@ -265,150 +428,14 @@ class CommandPathPost:
PathLog.debug("about to postprocess job: {}".format(job.Name))
wcslist = job.Fixtures
orderby = job.OrderOutputBy
split = job.SplitOutput
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 or split is True:
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)
postlist = self.buildPostList(job)
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)
for slist in postlist:
(fail, rc, filename) = self.exportObjectsWith(slist, job)
if fail:
break
self.subpart = 1

View File

@@ -25,7 +25,7 @@ import PathScripts
import PathScripts.post
import PathScripts.PathProfileContour
import PathScripts.PathJob
import PathScripts.PathPost
import PathScripts.PathPost as PathPost
import PathScripts.PathToolController
import PathScripts.PathUtil
import PathScripts.PostUtils as PostUtils
@@ -324,3 +324,303 @@ class TestPathPostImport(unittest.TestCase):
]
)
self.assertTrue(gcodeByToolNumberList[1][1] == 2)
class OutputOrderingTestCases(unittest.TestCase):
def setUp(self):
testfile = FreeCAD.getHomePath() + "Mod/Path/PathTests/boxtest.fcstd"
self.doc = FreeCAD.open(testfile)
self.job = FreeCAD.ActiveDocument.getObject("Job001")
def tearDown(self):
FreeCAD.closeDocument("boxtest")
def test010(self):
# Basic postprocessing:
self.job.Fixtures = ["G54"]
self.job.SplitOutput = False
self.job.OrderOutputBy = "Fixture"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
outlist = [i.Label for i in self.postlist[0]]
self.assertTrue(len(self.postlist) == 1)
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"T3",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
def test020(self):
# Multiple Fixtures
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = False
self.job.OrderOutputBy = "Fixture"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
self.assertTrue(len(self.postlist) == 1)
outlist = [i.Label for i in self.postlist[0]]
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"T3",
"FifthOp-(T3)",
"G55",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"T3",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
def test030(self):
# Multiple Fixtures - Split output
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = True
self.job.OrderOutputBy = "Fixture"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
self.assertTrue(len(self.postlist) == 2)
outlist = [i.Label for i in self.postlist[0]]
print(outlist)
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"T3",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
expected = [
"G55",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"T3",
"FifthOp-(T3)",
]
outlist = [i.Label for i in self.postlist[1]]
self.assertListEqual(outlist, expected)
def test040(self):
# Order by 'Tool'
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = False
self.job.OrderOutputBy = "Tool"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
outlist = [i.Label for i in self.postlist[0]]
self.assertTrue(len(self.postlist) == 1)
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"G55",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"G54",
"T3",
"FifthOp-(T3)",
"G55",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
def test050(self):
# Order by 'Tool' and split
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = True
self.job.OrderOutputBy = "Tool"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
outlist = [i.Label for i in self.postlist[0]]
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
"G55",
"FirstOp-(T1)",
"SecondOp-(T1)",
"T2",
"ThirdOp-(T2)",
"T1",
"FourthOp-(T1)",
]
self.assertListEqual(outlist, expected)
outlist = [i.Label for i in self.postlist[1]]
expected = [
"G54",
"T3",
"FifthOp-(T3)",
"G55",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
def test060(self):
# Order by 'Operation'
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = False
self.job.OrderOutputBy = "Operation"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
outlist = [i.Label for i in self.postlist[0]]
self.assertTrue(len(self.postlist) == 1)
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"G55",
"FirstOp-(T1)",
"G54",
"T1",
"SecondOp-(T1)",
"G55",
"SecondOp-(T1)",
"G54",
"T2",
"ThirdOp-(T2)",
"G55",
"ThirdOp-(T2)",
"G54",
"T1",
"FourthOp-(T1)",
"G55",
"FourthOp-(T1)",
"G54",
"T3",
"FifthOp-(T3)",
"G55",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)
def test070(self):
# Order by 'Operation' and split
self.job.Fixtures = ["G54", "G55"]
self.job.SplitOutput = True
self.job.OrderOutputBy = "Operation"
cpp = PathPost.CommandPathPost
self.postlist = cpp.buildPostList(self, self.job)
self.assertTrue(len(self.postlist) == 5)
outlist = [i.Label for i in self.postlist[0]]
expected = [
"G54",
"T1",
"FirstOp-(T1)",
"G55",
"FirstOp-(T1)",
]
self.assertListEqual(outlist, expected)
outlist = [i.Label for i in self.postlist[1]]
expected = [
"G54",
"T1",
"SecondOp-(T1)",
"G55",
"SecondOp-(T1)",
]
self.assertListEqual(outlist, expected)
outlist = [i.Label for i in self.postlist[2]]
expected = [
"G54",
"T2",
"ThirdOp-(T2)",
"G55",
"ThirdOp-(T2)",
]
self.assertListEqual(outlist, expected)
outlist = [i.Label for i in self.postlist[3]]
expected = [
"G54",
"T1",
"FourthOp-(T1)",
"G55",
"FourthOp-(T1)",
]
self.assertListEqual(outlist, expected)
outlist = [i.Label for i in self.postlist[4]]
expected = [
"G54",
"T3",
"FifthOp-(T3)",
"G55",
"FifthOp-(T3)",
]
self.assertListEqual(outlist, expected)

Binary file not shown.

View File

@@ -39,6 +39,7 @@ from PathTests.TestPathLog import TestPathLog
from PathTests.TestPathOpTools import TestPathOpTools
# from PathTests.TestPathPost import PathPostTestCases
from PathTests.TestPathPost import OutputOrderingTestCases
from PathTests.TestPathPost import TestPathPostUtils
from PathTests.TestPathPost import TestPathPostImport
from PathTests.TestPathPreferences import TestPathPreferences