Files
create/src/Mod/Path/PathScripts/PathSanity.py
Ton fc66fc1640 Update PathSanity.py
Output fillename for the report is now the same as the postprocessor job (job.PostProcessorOutputFile), this way the setup file will be autoloaded in LinuxCNC qtDraggon ui and others based on probe-basic.
There is some error checking missing, the check to see if the job.PostProcessorOutputFile is not empty is not there for example. But since the report can only be generated when there is a postprocessor file this is not a big problem.
2021-06-12 08:25:03 +02:00

752 lines
28 KiB
Python

# -*- coding: utf-8 -*-
# ***************************************************************************
# * Copyright (c) 2016 sliptonic <shopinthewoods@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * 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. *
# * *
# * FreeCAD 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 Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with FreeCAD; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
'''
This file has utilities for checking and catching common errors in FreeCAD
Path projects. Ideally, the user could execute these utilities from an icon
to make sure tools are selected and configured and defaults have been revised
'''
from __future__ import print_function
from PySide import QtCore, QtGui
import FreeCAD
import FreeCADGui
import PathScripts
import PathScripts.PathLog as PathLog
import PathScripts.PathUtil as PathUtil
import PathScripts.PathPreferences as PathPreferences
from collections import Counter
from datetime import datetime
import os
import webbrowser
# Qt translation handling
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
LOG_MODULE = 'PathSanity'
# PathLog.setLevel(PathLog.Level.INFO, LOG_MODULE)
# PathLog.trackModule('PathSanity')
class CommandPathSanity:
def resolveOutputPath(self, job):
if job.PostProcessorOutputFile != "":
filepath = job.PostProcessorOutputFile
elif PathPreferences.defaultOutputFile() != "":
filepath = PathPreferences.defaultOutputFile()
else:
filepath = PathPreferences.macroFilePath()
if '%D' in filepath:
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
filepath = filepath.replace('%D', D)
if '%d' in filepath:
d = FreeCAD.ActiveDocument.Label
filepath = filepath.replace('%d', d)
if '%j' in filepath:
j = job.Label
filepath = filepath.replace('%j', j)
if '%M' in filepath:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
filepath = filepath.replace('%M', M)
PathLog.debug('filepath: {}'.format(filepath))
# starting at the derived filename, iterate up until we have a valid
# directory to write to
while not os.path.isdir(filepath):
filepath = os.path.dirname(filepath)
PathLog.debug('filepath: {}'.format(filepath))
return filepath + os.sep
def GetResources(self):
return {'Pixmap': 'Path_Sanity',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Sanity",
"Check the path job for common errors"),
'Accel': "P, S",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Sanity",
"Check the path job for common errors")}
def IsActive(self):
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
return isinstance(obj.Proxy, PathScripts.PathJob.ObjectJob)
def Activated(self):
# if everything is ok, execute
self.squawkData = {"items": []}
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
self.outputpath = self.resolveOutputPath(obj)
data = self.__summarize(obj)
html = self.__report(data)
if html is not None:
FreeCAD.Console.PrintMessage("HTML report written to {}".format(html))
webbrowser.open(html)
def __makePicture(self, obj, imageName):
"""
Makes an image of the target object. Returns filename
"""
# remember vis state of document objects. Turn off all but target
visible = [o for o in obj.Document.Objects if o.Visibility]
for o in obj.Document.Objects:
o.Visibility = False
obj.Visibility = True
aview = FreeCADGui.activeDocument().activeView()
aview.setAnimationEnabled(False)
mw = FreeCADGui.getMainWindow()
mdi = mw.findChild(QtGui.QMdiArea)
view = mdi.activeSubWindow()
view.showNormal()
view.resize(320, 320)
imagepath = self.outputpath + '{}'.format(imageName)
aview.viewIsometric()
FreeCADGui.Selection.clearSelection()
FreeCADGui.SendMsgToActiveView("PerspectiveCamera")
FreeCADGui.Selection.addSelection(obj)
FreeCADGui.SendMsgToActiveView("ViewSelection")
FreeCADGui.Selection.clearSelection()
aview.saveImage(imagepath + '.png', 320, 320, 'Current')
aview.saveImage(imagepath + '_t.png', 320, 320, 'Transparent')
view.showMaximized()
aview.setAnimationEnabled(True)
# Restore visibility
obj.Visibility = False
for o in visible:
o.Visibility = True
return "{}_t.png".format(imagepath)
def __report(self, data):
"""
generates an asciidoc file with the report information
"""
reportTemplate = """
= Setup Report for FreeCAD Job: {jobname}
:toc:
:icons: font
:imagesdir: ""
:data-uri:
== Part Information
|===
{infoTable}
|===
== Run Summary
|===
{runTable}
|===
== Rough Stock
|===
{stockTable}
|===
== Tool Data
{toolTables}
== Output (Gcode)
|===
{outTable}
|===
== Fixtures and Workholding
|===
{fixtureTable}
|===
== Squawks
|===
{squawkTable}
|===
"""
# Generate the markup for the Part Information Section
infoTable = ""
PartLabel = translate("Path_Sanity", "Base Object(s)")
SequenceLabel = translate("Path_Sanity", "Job Sequence")
DescriptionLabel = translate("Path_Sanity", "Job Description")
JobTypeLabel = translate("Path_Sanity", "Job Type")
CADLabel = translate("Path_Sanity", "CAD File Name")
LastSaveLabel = translate("Path_Sanity", "Last Save Date")
CustomerLabel = translate("Path_Sanity", "Customer")
DesignerLabel = translate("Path_Sanity", "Designer")
d = data['designData']
b = data['baseData']
jobname = d['JobLabel']
basestable = "!===\n"
for key, val in b['bases'].items():
basestable += "! " + key + " ! " + val + "\n"
basestable += "!==="
infoTable += "|*" + PartLabel + "* a| " + basestable + " .7+a|" + \
"image::" + b['baseimage'] + "[" + PartLabel + "]\n"
infoTable += "|*" + SequenceLabel + "*|" + d['Sequence']
infoTable += "|*" + JobTypeLabel + "*|" + d['JobType']
infoTable += "|*" + DescriptionLabel + "*|" + d['JobDescription']
infoTable += "|*" + CADLabel + "*|" + d['FileName']
infoTable += "|*" + LastSaveLabel + "*|" + d['LastModifiedDate']
infoTable += "|*" + CustomerLabel + "*|" + d['Customer']
infoTable += "|*" + DesignerLabel + "*|" + d['Designer']
# Generate the markup for the Run Summary Section
runTable = ""
opLabel = translate("Path_Sanity", "Operation")
zMinLabel = translate("Path_Sanity", "Minimum Z Height")
zMaxLabel = translate("Path_Sanity", "Maximum Z Height")
cycleTimeLabel = translate("Path_Sanity", "Cycle Time")
coolantLabel = translate("Path_Sanity", "Coolant")
jobTotalLabel = translate("Path_Sanity", "TOTAL JOB")
d = data['runData']
runTable += "|*" + opLabel + "*|*" + zMinLabel + "*|*" + zMaxLabel + \
"*|*" + coolantLabel + "*|*" + cycleTimeLabel + "*\n"
for i in d['items']:
runTable += "|{}".format(i['opName'])
runTable += "|{}".format(i['minZ'])
runTable += "|{}".format(i['maxZ'])
runTable += "|{}".format(i['coolantMode'])
runTable += "|{}".format(i['cycleTime'])
runTable += "|*" + jobTotalLabel + "* |{} |{} |{}".format(
d['jobMinZ'],
d['jobMaxZ'],
d['cycletotal'])
# Generate the markup for the Tool Data Section
toolTables = ""
toolLabel = translate("Path_Sanity", "Tool Number")
descriptionLabel = translate("Path_Sanity", "Description")
manufLabel = translate("Path_Sanity", "Manufacturer")
partNumberLabel = translate("Path_Sanity", "Part Number")
urlLabel = translate("Path_Sanity", "URL")
inspectionNotesLabel = translate("Path_Sanity", "Inspection Notes")
opLabel = translate("Path_Sanity", "Operation")
tcLabel = translate("Path_Sanity", "Tool Controller")
feedLabel = translate("Path_Sanity", "Feed Rate")
speedLabel = translate("Path_Sanity", "Spindle Speed")
shapeLabel = translate("Path_Sanity", "Tool Shape")
diameterLabel = translate("Path_Sanity", "Tool Diameter")
d = data['toolData']
for key, value in d.items():
toolTables += "=== {}: T{}\n".format(toolLabel, key)
toolTables += "|===\n"
toolTables += "|*{}*| {} a| image::{}[{}]\n".format(descriptionLabel, value['description'], value['imagepath'], key)
toolTables += "|*{}* 2+| {}\n".format(manufLabel, value['manufacturer'])
toolTables += "|*{}* 2+| {}\n".format(partNumberLabel, value['partNumber'])
toolTables += "|*{}* 2+| {}\n".format(urlLabel, value['url'])
toolTables += "|*{}* 2+| {}\n".format(inspectionNotesLabel, value['inspectionNotes'])
toolTables += "|*{}* 2+| {}\n".format(shapeLabel, value['shape'])
toolTables += "|*{}* 2+| {}\n".format(diameterLabel, value['diameter'])
toolTables += "|===\n"
toolTables += "|===\n"
toolTables += "|*" + opLabel + "*|*" + tcLabel + "*|*" + feedLabel + "*|*" + speedLabel + "*\n"
for o in value['ops']:
toolTables += "|" + o['Operation'] + "|" + o['ToolController'] + "|" + o['Feed'] + "|" + o['Speed'] + "\n"
toolTables += "|===\n"
toolTables += "\n"
# Generate the markup for the Rough Stock Section
stockTable = ""
xDimLabel = translate("Path_Sanity", "X Size")
yDimLabel = translate("Path_Sanity", "Y Size")
zDimLabel = translate("Path_Sanity", "Z Size")
materialLabel = translate("Path_Sanity", "Material")
d = data['stockData']
stockTable += "|*{}*|{} .4+a|image::{}[stock]\n".format(
materialLabel,
d['material'],
d['stockImage'])
stockTable += "|*{}*|{}".format(xDimLabel, d['xLen'])
stockTable += "|*{}*|{}".format(yDimLabel, d['yLen'])
stockTable += "|*{}*|{}".format(zDimLabel, d['zLen'])
# Generate the markup for the Fixture Section
fixtureTable = ""
offsetsLabel = translate("Path_Sanity", "Work Offsets")
orderByLabel = translate("Path_Sanity", "Order By")
datumLabel = translate("Path_Sanity", "Part Datum")
d = data['fixtureData']
fixtureTable += "|*{}*|{}\n".format(offsetsLabel, d['fixtures'])
fixtureTable += "|*{}*|{}\n".format(orderByLabel, d['orderBy'])
fixtureTable += "|*{}* a| image::{}[]".format(datumLabel, d['datumImage'])
# Generate the markup for the Output Section
outTable = ""
d = data['outputData']
gcodeFileLabel = translate("Path_Sanity", "Gcode File")
lastpostLabel = translate("Path_Sanity", "Last Post Process Date")
stopsLabel = translate("Path_Sanity", "Stops")
programmerLabel = translate("Path_Sanity", "Programmer")
machineLabel = translate("Path_Sanity", "Machine")
postLabel = translate("Path_Sanity", "Postprocessor")
flagsLabel = translate("Path_Sanity", "Post Processor Flags")
fileSizeLabel = translate("Path_Sanity", "File Size (kbs)")
lineCountLabel = translate("Path_Sanity", "Line Count")
outTable += "|*{}*|{}\n".format(gcodeFileLabel, d['lastgcodefile'])
outTable += "|*{}*|{}\n".format(lastpostLabel, d['lastpostprocess'])
outTable += "|*{}*|{}\n".format(stopsLabel, d['optionalstops'])
outTable += "|*{}*|{}\n".format(programmerLabel, d['programmer'])
outTable += "|*{}*|{}\n".format(machineLabel, d['machine'])
outTable += "|*{}*|{}\n".format(postLabel, d['postprocessor'])
outTable += "|*{}*|{}\n".format(flagsLabel, d['postprocessorFlags'])
outTable += "|*{}*|{}\n".format(fileSizeLabel, d['filesize'])
outTable += "|*{}*|{}\n".format(lineCountLabel, d['linecount'])
# Generate the markup for the Squawk Section
noteLabel = translate("Path_Sanity", "Note")
operatorLabel = translate("Path_Sanity", "Operator")
dateLabel = translate("Path_Sanity", "Date")
squawkTable = "|*{}*|*{}*|*{}*\n".format(noteLabel,
operatorLabel,
dateLabel)
d = data['squawkData']
for i in d['items']:
squawkTable += "a|{}: {}".format(i['squawkType'], i['Note'])
squawkTable += "|{}".format(i['Operator'])
squawkTable += "|{}".format(i['Date'])
squawkTable += "\n"
# merge template and custom markup
report = reportTemplate.format(
jobname=jobname,
infoTable=infoTable,
runTable=runTable,
toolTables=toolTables,
stockTable=stockTable,
fixtureTable=fixtureTable,
outTable=outTable,
squawkTable=squawkTable)
# Save the report
reportraw = self.outputpath + job.PostProcessorOutputFile + '.asciidoc'
reporthtml = self.outputpath + job.PostProcessorOutputFile + '.html'
with open(reportraw, 'w') as fd:
fd.write(report)
fd.close()
FreeCAD.Console.PrintMessage('asciidoc file written to {}\n'.format(reportraw))
try:
result = os.system('asciidoctor {} -o {}'.format(reportraw,
reporthtml))
if str(result) == "32512":
msg = "asciidoctor not found. html cannot be generated."
QtGui.QMessageBox.information(None, "Path Sanity", msg)
FreeCAD.Console.PrintMessage(msg)
reporthtml = None
except Exception as e:
FreeCAD.Console.PrintError(e)
return reporthtml
def __summarize(self, obj):
"""
Top level function to summarize information for the report
Returns a dictionary of sections
"""
data = {}
data['baseData'] = self.__baseObjectData(obj)
data['designData'] = self.__designData(obj)
data['toolData'] = self.__toolData(obj)
data['runData'] = self.__runData(obj)
data['outputData'] = self.__outputData(obj)
data['fixtureData'] = self.__fixtureData(obj)
data['stockData'] = self.__stockData(obj)
data['squawkData'] = self.squawkData
return data
def squawk(self, operator, note, date=datetime.now(), squawkType="NOTE"):
squawkType = squawkType if squawkType in \
["NOTE", "WARNING", "CAUTION", "TIP"] else "NOTE"
self.squawkData['items'].append({"Date": str(date),
"Operator": operator,
"Note": note,
"squawkType": squawkType})
def __baseObjectData(self, obj):
data = {'baseimage': '',
'bases': ''}
try:
bases = {}
for name, count in \
PathUtil.keyValueIter(Counter([obj.Proxy.baseObject(obj,
o).Label for o in obj.Model.Group])):
bases[name] = str(count)
data['baseimage'] = self.__makePicture(obj.Model, "baseimage")
data['bases'] = bases
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__baseObjectData)", e, squawkType="CAUTION")
return data
def __designData(self, obj):
"""
Returns header information about the design document
Returns information about issues and concerns (squawks)
"""
data = {'FileName': '',
'LastModifiedDate': '',
'Customer': '',
'Designer': '',
'JobDescription': '',
'JobLabel': '',
'Sequence': '',
'JobType': ''}
try:
data['FileName'] = obj.Document.FileName
data['LastModifiedDate'] = str(obj.Document.LastModifiedDate)
data['Customer'] = obj.Document.Company
data['Designer'] = obj.Document.LastModifiedBy
data['JobDescription'] = obj.Description
data['JobLabel'] = obj.Label
n = 0
m = 0
for i in obj.Document.Objects:
if hasattr(i, "Proxy"):
if isinstance(i.Proxy, PathScripts.PathJob.ObjectJob):
m += 1
if i is obj:
n = m
data['Sequence'] = "{} of {}".format(n, m)
data['JobType'] = "2.5D Milling" # improve after job types added
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__designData)", e, squawkType="CAUTION")
return data
def __toolData(self, obj):
"""
Returns information about the tools used in the job, and associated
toolcontrollers
Returns information about issues and problems with the tools (squawks)
"""
data = {}
try:
for TC in obj.Tools.Group:
if not hasattr(TC.Tool, 'BitBody'):
self.squawk("PathSanity",
"Tool number {} is a legacy tool. Legacy tools not \
supported by Path-Sanity".format(TC.ToolNumber),
squawkType="WARNING")
continue # skip old-style tools
tooldata = data.setdefault(str(TC.ToolNumber), {})
bitshape = tooldata.setdefault('BitShape', "")
if bitshape not in ["", TC.Tool.BitShape]:
self.squawk("PathSanity",
"Tool number {} used by multiple tools".format(TC.ToolNumber),
squawkType="CAUTION")
tooldata['bitShape'] = TC.Tool.BitShape
tooldata['description'] = TC.Tool.Label
tooldata['manufacturer'] = ""
tooldata['url'] = ""
tooldata['inspectionNotes'] = ""
tooldata['diameter'] = str(TC.Tool.Diameter)
tooldata['shape'] = TC.Tool.ShapeName
tooldata['partNumber'] = ""
imagedata = TC.Tool.Proxy.getBitThumbnail(TC.Tool)
imagepath = '{}T{}.png'.format(self.outputpath, TC.ToolNumber)
tooldata['feedrate'] = str(TC.HorizFeed)
if TC.HorizFeed.Value == 0.0:
self.squawk("PathSanity",
"Tool Controller '{}' has no feedrate".format(TC.Label),
squawkType="WARNING")
tooldata['spindlespeed'] = str(TC.SpindleSpeed)
if TC.SpindleSpeed == 0.0:
self.squawk("PathSanity",
"Tool Controller '{}' has no spindlespeed".format(TC.Label),
squawkType="WARNING")
with open(imagepath, 'wb') as fd:
fd.write(imagedata)
fd.close()
tooldata['imagepath'] = imagepath
used = False
for op in obj.Operations.Group:
if hasattr(op, "ToolController") and op.ToolController is TC:
used = True
tooldata.setdefault('ops', []).append(
{"Operation": op.Label,
"ToolController": TC.Label,
"Feed": str(TC.HorizFeed),
"Speed": str(TC.SpindleSpeed)})
if used is False:
tooldata.setdefault('ops', [])
self.squawk("PathSanity",
"Tool Controller '{}' is not used".format(TC.Label),
squawkType="WARNING")
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__toolData)", e, squawkType="CAUTION")
return data
def __runData(self, obj):
data = {'cycletotal': '',
'jobMinZ': '',
'jobMaxZ': '',
'jobDescription': '',
'items': []}
try:
data['cycletotal'] = str(obj.CycleTime)
data['jobMinZ'] = FreeCAD.Units.Quantity(obj.Path.BoundBox.ZMin,
FreeCAD.Units.Length).UserString
data['jobMaxZ'] = FreeCAD.Units.Quantity(obj.Path.BoundBox.ZMax,
FreeCAD.Units.Length).UserString
data['jobDescription'] = obj.Description
data['items'] = []
for op in obj.Operations.Group:
oplabel = op.Label
ctime = op.CycleTime if hasattr(op, "CycleTime") else 0.0
cool = op.CoolantMode if hasattr(op, "CoolantMode") else "N/A"
o = op
while len(o.ViewObject.claimChildren()) != 0: # dressup
oplabel = "{}:{}".format(oplabel, o.Base.Label)
o = o.Base
if hasattr(o, 'CycleTime'):
ctime = o.CycleTime
cool = o.CoolantMode if hasattr(o, "CoolantMode") else cool
if hasattr(op, 'Active') and not op.Active:
oplabel = "{} (INACTIVE)".format(oplabel)
ctime = 0.0
if op.Path.BoundBox.isValid():
zmin = FreeCAD.Units.Quantity(op.Path.BoundBox.ZMin,
FreeCAD.Units.Length).UserString
zmax = FreeCAD.Units.Quantity(op.Path.BoundBox.ZMax,
FreeCAD.Units.Length).UserString
else:
zmin = ''
zmax = ''
opdata = {"opName": oplabel,
"minZ": zmin,
"maxZ": zmax,
"cycleTime": ctime,
"coolantMode": cool}
data['items'].append(opdata)
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__runData)", e, squawkType="CAUTION")
return data
def __stockData(self, obj):
data = {'xLen': '',
'yLen': '',
'zLen': '',
'material': '',
'stockImage': ''}
try:
bb = obj.Stock.Shape.BoundBox
data['xLen'] = FreeCAD.Units.Quantity(bb.XLength, FreeCAD.Units.Length).UserString
data['yLen'] = FreeCAD.Units.Quantity(bb.YLength, FreeCAD.Units.Length).UserString
data['zLen'] = FreeCAD.Units.Quantity(bb.ZLength, FreeCAD.Units.Length).UserString
data['material'] = "Not Specified"
if hasattr(obj.Stock, 'Material'):
if obj.Stock.Material is not None:
data['material'] = obj.Stock.Material.Material['Name']
if data['material'] == "Not Specified":
self.squawk("PathSanity", "Consider Specifying the Stock Material", squawkType="TIP")
data['stockImage'] = self.__makePicture(obj.Stock, "stockImage")
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__stockData)", e, squawkType="CAUTION")
return data
def __fixtureData(self, obj):
data = {'fixtures': '', 'orderBy': '', 'datumImage': ''}
try:
data['fixtures'] = str(obj.Fixtures)
data['orderBy'] = str(obj.OrderOutputBy)
aview = FreeCADGui.activeDocument().activeView()
aview.setAnimationEnabled(False)
obj.Visibility = False
obj.Operations.Visibility = False
mw = FreeCADGui.getMainWindow()
mdi = mw.findChild(QtGui.QMdiArea)
view = mdi.activeSubWindow()
view.showNormal()
view.resize(320, 320)
imagepath = '{}origin'.format(self.outputpath)
FreeCADGui.Selection.clearSelection()
FreeCADGui.SendMsgToActiveView("PerspectiveCamera")
aview.viewIsometric()
for i in obj.Model.Group:
FreeCADGui.Selection.addSelection(i)
FreeCADGui.SendMsgToActiveView("ViewSelection")
FreeCADGui.Selection.clearSelection()
obj.ViewObject.Proxy.editObject(obj)
aview.saveImage('{}.png'.format(imagepath), 320, 320, 'Current')
aview.saveImage('{}_t.png'.format(imagepath), 320, 320, 'Transparent')
obj.ViewObject.Proxy.uneditObject(obj)
obj.Visibility = True
obj.Operations.Visibility = True
view.showMaximized()
aview.setAnimationEnabled(True)
data['datumImage'] = '{}_t.png'.format(imagepath)
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__fixtureData)", e, squawkType="CAUTION")
return data
def __outputData(self, obj):
data = {'lastpostprocess': '',
'lastgcodefile': '',
'optionalstops': '',
'programmer': '',
'machine': '',
'postprocessor': '',
'postprocessorFlags': '',
'filesize': '',
'linecount': ''}
try:
data['lastpostprocess'] = str(obj.LastPostProcessDate)
data['lastgcodefile'] = str(obj.LastPostProcessOutput)
data['optionalstops'] = "False"
data['programmer'] = ""
data['machine'] = ""
data['postprocessor'] = str(obj.PostProcessor)
data['postprocessorFlags'] = str(obj.PostProcessorArgs)
for op in obj.Operations.Group:
if isinstance(op.Proxy, PathScripts.PathStop.Stop) and op.Stop is True:
data['optionalstops'] = "True"
if obj.LastPostProcessOutput == "":
data['filesize'] = str(0.0)
data['linecount'] = str(0)
self.squawk("PathSanity", "The Job has not been post-processed")
else:
data['filesize'] = str(os.path.getsize(obj.LastPostProcessOutput))
data['linecount'] = str(sum(1 for line in open(obj.LastPostProcessOutput)))
except Exception as e:
data['errors'] = e
self.squawk("PathSanity(__outputData)", e, squawkType="CAUTION")
return data
if FreeCAD.GuiUp:
# register the FreeCAD command
FreeCADGui.addCommand('Path_Sanity', CommandPathSanity())