Working html report with asciidoctor
This commit is contained in:
@@ -32,14 +32,14 @@ import PathScripts.PathToolController as PathToolController
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import json
|
||||
import time
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
ArchPanel = LazyLoader('ArchPanel', globals(), 'ArchPanel')
|
||||
Draft = LazyLoader('Draft', globals(), 'Draft')
|
||||
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
@@ -103,6 +103,10 @@ class ObjectJob:
|
||||
obj.addProperty("App::PropertyFile", "PostProcessorOutputFile", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "The NC output file for this project"))
|
||||
obj.addProperty("App::PropertyEnumeration", "PostProcessor", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Post Processor"))
|
||||
obj.addProperty("App::PropertyString", "PostProcessorArgs", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Arguments for the Post Processor (specific to the script)"))
|
||||
obj.addProperty("App::PropertyString", "LastPostProcessDate", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"))
|
||||
obj.setEditorMode('LastPostProcessDate', 2) # Hide
|
||||
obj.addProperty("App::PropertyString", "LastPostProcessOutput", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"))
|
||||
obj.setEditorMode('LastPostProcessOutput', 2) # Hide
|
||||
|
||||
obj.addProperty("App::PropertyString", "Description", "Path", QtCore.QT_TRANSLATE_NOOP("PathJob", "An optional description for this job"))
|
||||
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Job Cycle Time Estimation"))
|
||||
@@ -226,7 +230,7 @@ class ObjectJob:
|
||||
# Tool controllers might refer to either legacy tool or toolbit
|
||||
PathLog.debug('taking down tool controller')
|
||||
for tc in obj.ToolController:
|
||||
if hasattr(tc.Tool,"Proxy"):
|
||||
if hasattr(tc.Tool, "Proxy"):
|
||||
PathUtil.clearExpressionEngine(tc.Tool)
|
||||
doc.removeObject(tc.Tool.Name)
|
||||
PathUtil.clearExpressionEngine(tc)
|
||||
@@ -378,7 +382,7 @@ class ObjectJob:
|
||||
try:
|
||||
# Convert the formatted time from HH:MM:SS to just seconds
|
||||
opCycleTime = sum(x * int(t) for x, t in zip([1, 60, 3600], reversed(formattedCycleTime.split(":"))))
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if opCycleTime > 0:
|
||||
@@ -457,7 +461,7 @@ def Instances():
|
||||
def Create(name, base, templateFile=None):
|
||||
'''Create(name, base, templateFile=None) ... creates a new job and all it's resources.
|
||||
If a template file is specified the new job is initialized with the values from the template.'''
|
||||
if str == type(base[0]):
|
||||
if isinstance(base[0], str):
|
||||
models = []
|
||||
for baseName in base:
|
||||
models.append(FreeCAD.ActiveDocument.getObject(baseName))
|
||||
|
||||
@@ -37,7 +37,7 @@ import os
|
||||
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
LOG_MODULE = PathLog.thisModule()
|
||||
|
||||
@@ -210,9 +210,9 @@ class CommandPathPost:
|
||||
print("post: %s(%s, %s)" % (postname, filename, postArgs))
|
||||
processor = PostProcessor.load(postname)
|
||||
gcode = processor.export(objs, filename, postArgs)
|
||||
return (False, gcode)
|
||||
return (False, gcode, filename)
|
||||
else:
|
||||
return (True, '')
|
||||
return (True, '', filename)
|
||||
|
||||
def Activated(self):
|
||||
PathLog.track()
|
||||
@@ -391,17 +391,22 @@ class CommandPathPost:
|
||||
rc = '' # pylint: disable=unused-variable
|
||||
if split:
|
||||
for slist in postlist:
|
||||
(fail, rc) = self.exportObjectsWith(slist, job)
|
||||
(fail, rc, filename) = self.exportObjectsWith(slist, job)
|
||||
else:
|
||||
finalpostlist = [item for slist in postlist for item in slist]
|
||||
(fail, rc) = self.exportObjectsWith(finalpostlist, job)
|
||||
(fail, rc, filename) = self.exportObjectsWith(finalpostlist, job)
|
||||
|
||||
self.subpart = 1
|
||||
|
||||
if fail:
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
else:
|
||||
if hasattr(job, "LastPostProcessDate"):
|
||||
job.LastPostProcessDate = str(datetime.now())
|
||||
if hasattr(job, "LastPostProcessOutput"):
|
||||
job.LastPostProcessOutput = filename
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
|
||||
@@ -19,146 +19,671 @@
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************/
|
||||
# ***************************************************************************
|
||||
|
||||
'''This file has utilities for checking and catching common errors in FreeCAD
|
||||
'''
|
||||
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'''
|
||||
to make sure tools are selected and configured and defaults have been revised
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
from PySide import QtCore
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathScripts
|
||||
import PathScripts.PathLog as PathLog
|
||||
# import PathScripts.PathCollision as PC
|
||||
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')
|
||||
# PathLog.trackModule('PathSanity')
|
||||
|
||||
|
||||
class CommandPathSanity:
|
||||
baseobj=None
|
||||
baseobj = None
|
||||
outputpath = PathPreferences.defaultOutputFile()
|
||||
squawkData = {"items": []}
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'Path-Sanity',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Sanity","Check the Path project for common errors"),
|
||||
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 Project for common errors")}
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Sanity",
|
||||
"Check the path job for common errors")}
|
||||
|
||||
def IsActive(self):
|
||||
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
|
||||
if hasattr(obj, 'Operations') and hasattr(obj, 'ToolController'):
|
||||
return True
|
||||
return False
|
||||
return isinstance(obj.Proxy, PathScripts.PathJob.ObjectJob)
|
||||
|
||||
def Activated(self):
|
||||
# if everything is ok, execute
|
||||
obj = FreeCADGui.Selection.getSelectionEx()[0].Object
|
||||
self.baseobj = obj.Base
|
||||
if self.baseobj is None:
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "The Job has no selected Base object.")+"\n")
|
||||
return
|
||||
self.__review(obj)
|
||||
data = self.__summarize(obj)
|
||||
html = self.__report(data)
|
||||
if html is not None:
|
||||
print(html)
|
||||
webbrowser.open(html)
|
||||
|
||||
def __review(self, obj):
|
||||
"checks the selected job for common errors"
|
||||
clean = True
|
||||
def __makePicture(self, obj, imageName):
|
||||
"""
|
||||
Makes an image of the target object. Returns filename
|
||||
"""
|
||||
|
||||
# if obj.X_Max == obj.X_Min or obj.Y_Max == obj.Y_Min:
|
||||
# FreeCAD.Console.PrintWarning(translate("Path_Sanity", "It appears the machine limits haven't been set. Not able to check path extents.")+"\n")
|
||||
# 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
|
||||
|
||||
if obj.PostProcessor == '':
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "A Postprocessor has not been selected.")+"\n")
|
||||
clean = False
|
||||
aview = FreeCADGui.activeDocument().activeView()
|
||||
aview.setAnimationEnabled(False)
|
||||
|
||||
if obj.PostProcessorOutputFile == '':
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "No output file is named. You'll be prompted during postprocessing.")+"\n")
|
||||
clean = False
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
mdi = mw.findChild(QtGui.QMdiArea)
|
||||
view = mdi.activeSubWindow()
|
||||
view.showNormal()
|
||||
view.resize(320, 320)
|
||||
|
||||
for tc in obj.ToolController:
|
||||
PathLog.info("Checking: {}.{}".format(obj.Label, tc.Label))
|
||||
clean &= self.__checkTC(tc)
|
||||
imagepath = self.outputpath + '/{}'.format(imageName)
|
||||
|
||||
for op in obj.Operations.Group:
|
||||
PathLog.info("Checking: {}.{}".format(obj.Label, op.Label))
|
||||
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')
|
||||
|
||||
if isinstance(op.Proxy, PathScripts.PathProfileContour.ObjectContour):
|
||||
if op.Active:
|
||||
# simobj = op.Proxy.execute(op, getsim=True)
|
||||
# if simobj is not None:
|
||||
# print ('collision detected')
|
||||
# PC.getCollisionObject(self.baseobj, simobj)
|
||||
# clean = False
|
||||
pass
|
||||
view.showMaximized()
|
||||
|
||||
if isinstance(op.Proxy, PathScripts.PathProfileFaces.ObjectProfile):
|
||||
if op.Active:
|
||||
# simobj = op.Proxy.execute(op, getsim=True)
|
||||
# if simobj is not None:
|
||||
# print ('collision detected')
|
||||
# PC.getCollisionObject(self.baseobj, simobj)
|
||||
# clean = False
|
||||
pass
|
||||
aview.setAnimationEnabled(True)
|
||||
|
||||
if isinstance(op.Proxy, PathScripts.PathProfileEdges.ObjectProfile):
|
||||
if op.Active:
|
||||
# simobj = op.Proxy.execute(op, getsim=True)
|
||||
# if simobj is not None:
|
||||
# print ('collision detected')
|
||||
# PC.getCollisionObject(self.baseobj, simobj)
|
||||
# clean = False
|
||||
pass
|
||||
# Restore visibility
|
||||
obj.Visibility = False
|
||||
for o in visible:
|
||||
o.Visibility = True
|
||||
|
||||
if isinstance(op.Proxy, PathScripts.PathPocket.ObjectPocket):
|
||||
if op.Active:
|
||||
# simobj = op.Proxy.execute(op, getsim=True)
|
||||
# if simobj is not None:
|
||||
# print ('collision detected')
|
||||
# PC.getCollisionObject(self.baseobj, simobj)
|
||||
# clean = False
|
||||
pass
|
||||
# with open(imagepath, 'wb') as fd:
|
||||
# fd.write(imagedata)
|
||||
# fd.close()
|
||||
|
||||
if isinstance(op.Proxy, PathScripts.PathPocketShape.ObjectPocket):
|
||||
if op.Active:
|
||||
# simobj = op.Proxy.execute(op, getsim=True)
|
||||
# if simobj is not None:
|
||||
# print ('collision detected')
|
||||
# PC.getCollisionObject(self.baseobj, simobj)
|
||||
# clean = False
|
||||
pass
|
||||
return "{}_t.png".format(imagepath)
|
||||
|
||||
if not any(op.Active for op in obj.Operations.Group): #no active operations
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "No active operations was found. Post processing will not result in any tooling."))
|
||||
clean = False
|
||||
def __report(self, data):
|
||||
"""
|
||||
generates an asciidoc file with the report information
|
||||
"""
|
||||
|
||||
if len(obj.ToolController) == 0: #need at least one active TC
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "A Tool Controller was not found. Default values are used which is dangerous. Please add a Tool Controller.")+"\n")
|
||||
clean = False
|
||||
reportTemplate = """
|
||||
= Setup Report for FreeCAD Job: {jobname}
|
||||
:toc:
|
||||
:icons: font
|
||||
:imagesdir: ""
|
||||
:data-uri:
|
||||
|
||||
if clean:
|
||||
FreeCAD.Console.PrintMessage(translate("Path_Sanity", "No issues detected, {} has passed basic sanity check.").format(obj.Label))
|
||||
== Part Information
|
||||
|
||||
|===
|
||||
{infoTable}
|
||||
|===
|
||||
|
||||
|
||||
== Run Summary
|
||||
|
||||
|===
|
||||
{runTable}
|
||||
|===
|
||||
|
||||
== Rough Stock
|
||||
|
||||
|===
|
||||
{stockTable}
|
||||
|===
|
||||
|
||||
== Tool Data
|
||||
|
||||
{toolTables}
|
||||
|
||||
== Output (Gcode)
|
||||
|
||||
|===
|
||||
{outTable}
|
||||
|===
|
||||
|
||||
== Coolant
|
||||
|
||||
|===
|
||||
{coolantTable}
|
||||
|===
|
||||
|
||||
== 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")
|
||||
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 += "|*" + 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")
|
||||
jobTotalLabel = translate("Path_Sanity", "TOTAL JOB")
|
||||
|
||||
d = data['runData']
|
||||
|
||||
runTable += "|*" + opLabel + "*|*" + zMinLabel + "*|*" + zMaxLabel + \
|
||||
"*|*" + cycleTimeLabel + "*\n"
|
||||
|
||||
for i in d['items']:
|
||||
runTable += "|{}".format(i['opName'])
|
||||
runTable += "|{}".format(i['minZ'])
|
||||
runTable += "|{}".format(i['maxZ'])
|
||||
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 += "|*" + toolLabel + "*| T" + key + " .2+a|" + "image::" + value['imagepath'] + "[" + key + "]|\n"
|
||||
|
||||
toolTables += "|*" + descriptionLabel + "*|" + value['description'] + " a|" + "image::" + value['imagepath'] + "[" + key + "]\n"
|
||||
toolTables += "|*" + manufLabel + "* 2+|" + value['manufacturer'] + "\n"
|
||||
toolTables += "|*" + partNumberLabel + "* 2+|" + value['partNumber'] + "\n"
|
||||
toolTables += "|*" + urlLabel + "* 2+|" + value['url'] + "\n"
|
||||
toolTables += "|*" + inspectionNotesLabel + "* 2+|" + value['inspectionNotes'] + "\n"
|
||||
toolTables += "|*" + shapeLabel + "* 2+|" + value['shape'] + "\n"
|
||||
toolTables += "|*" + diameterLabel + "* 2+|" + value['diameter'] + "\n"
|
||||
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 += "|*" + materialLabel + "*|" + d['material'] + \
|
||||
" .4+a|" + "image::" + d['stockImage'] + "[stock]\n"
|
||||
stockTable += "|*" + xDimLabel + "*|" + d['xLen']
|
||||
stockTable += "|*" + yDimLabel + "*|" + d['yLen']
|
||||
stockTable += "|*" + 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 += "|*" + offsetsLabel + "*|" + d['fixtures'] + "\n"
|
||||
fixtureTable += "|*" + orderByLabel + "*|" + d['orderBy']
|
||||
fixtureTable += "|*" + datumLabel + "* a|image::" + d['datumImage'] + "[]"
|
||||
|
||||
# Generate the markup for the Coolant Section
|
||||
|
||||
coolantTable = ""
|
||||
|
||||
opLabel = translate("Path_Sanity", "Operation")
|
||||
coolantLabel = translate("Path_Sanity", "Coolant Mode")
|
||||
|
||||
d = data['coolantData']['items']
|
||||
|
||||
coolantTable += "|*" + opLabel + "*|*" + coolantLabel + "*\n"
|
||||
|
||||
for i in d:
|
||||
coolantTable += "|" + i['opName']
|
||||
coolantTable += "|" + i['CoolantMode']
|
||||
|
||||
# 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 += "|*" + gcodeFileLabel + "*|" + d['lastgcodefile'] + "\n"
|
||||
outTable += "|*" + lastpostLabel + "*|" + d['lastpostprocess'] + "\n"
|
||||
outTable += "|*" + stopsLabel + "*|" + d['optionalstops'] + "\n"
|
||||
outTable += "|*" + programmerLabel + "*|" + d['programmer'] + "\n"
|
||||
outTable += "|*" + machineLabel + "*|" + d['machine'] + "\n"
|
||||
outTable += "|*" + postLabel + "*|" + d['postprocessor'] + "\n"
|
||||
outTable += "|*" + flagsLabel + "*|" + d['postprocessorFlags'] + "\n"
|
||||
outTable += "|*" + fileSizeLabel + "*|" + d['filesize'] + "\n"
|
||||
outTable += "|*" + lineCountLabel + "*|" + d['linecount'] + "\n"
|
||||
|
||||
# Generate the markup for the Squawk Section
|
||||
|
||||
noteLabel = translate("Path_Sanity", "Note")
|
||||
operatorLabel = translate("Path_Sanity", "Operator")
|
||||
dateLabel = translate("Path_Sanity", "Date")
|
||||
|
||||
squawkTable = ""
|
||||
squawkTable += "|*" + noteLabel + "*|*" + operatorLabel + "*|*" + dateLabel + "*\n"
|
||||
|
||||
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,
|
||||
coolantTable=coolantTable,
|
||||
squawkTable=squawkTable)
|
||||
|
||||
# Save the report
|
||||
|
||||
reportraw = self.outputpath + '/setupreport.asciidoc'
|
||||
reporthtml = self.outputpath + '/setupreport.html'
|
||||
with open(reportraw, 'w') as fd:
|
||||
fd.write(report)
|
||||
fd.close()
|
||||
|
||||
try:
|
||||
result = os.system('asciidoctor {} -o {}'.format(reportraw, reporthtml))
|
||||
if str(result) == "32512":
|
||||
print('asciidoctor not found')
|
||||
reporthtml = None
|
||||
|
||||
except Exception as e:
|
||||
print(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['coolantData'] = self.__coolantData(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", "ERROR", "TIP"] else "NOTE"
|
||||
|
||||
self.squawkData['items'].append({"Date": str(date),
|
||||
"Operator": operator,
|
||||
"Note": note,
|
||||
"squawkType": squawkType})
|
||||
|
||||
def __baseObjectData(self, obj):
|
||||
data = {}
|
||||
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
|
||||
|
||||
return data
|
||||
|
||||
def __designData(self, obj):
|
||||
"""
|
||||
Returns header information about the design document
|
||||
Returns information about issues and concerns (squawks)
|
||||
"""
|
||||
|
||||
data = {}
|
||||
try:
|
||||
data['FileName'] = obj.Document.FileName
|
||||
data['LastModifiedDate'] = str(obj.Document.LastModifiedDate)
|
||||
data['Customer'] = obj.Document.Company
|
||||
data['Designer'] = obj.Document.LastModifiedBy
|
||||
data['JobNotes'] = 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
|
||||
|
||||
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.ToolController:
|
||||
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="ERROR")
|
||||
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 op.ToolController is TC:
|
||||
used = True
|
||||
tooldata.setdefault('ops', []).append(
|
||||
{"Operation": op.Label,
|
||||
"ToolController": TC.Name,
|
||||
"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
|
||||
|
||||
return data
|
||||
|
||||
def __runData(self, obj):
|
||||
data = {}
|
||||
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['items'] = []
|
||||
for op in obj.Operations.Group:
|
||||
oplabel = op.Label if op.Active else op.Label + " (INACTIVE)"
|
||||
opdata = {"opName": oplabel,
|
||||
"minZ": FreeCAD.Units.Quantity(op.Path.BoundBox.ZMin,
|
||||
FreeCAD.Units.Length).UserString,
|
||||
"maxZ": FreeCAD.Units.Quantity(op.Path.BoundBox.ZMax,
|
||||
FreeCAD.Units.Length).UserString,
|
||||
#"maxZ": str(op.Path.BoundBox.ZMax),
|
||||
"cycleTime": str(op.CycleTime)}
|
||||
data['items'].append(opdata)
|
||||
|
||||
except Exception as e:
|
||||
data['errors'] = e
|
||||
|
||||
return data
|
||||
|
||||
def __stockData(self, obj):
|
||||
data = {}
|
||||
|
||||
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" # fix this
|
||||
|
||||
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
|
||||
|
||||
return data
|
||||
|
||||
def __coolantData(self, obj):
|
||||
data = {"items": []}
|
||||
|
||||
try:
|
||||
for op in obj.Operations.Group:
|
||||
opLabel = op.Label if op.Active else op.Label + " (INACTIVE)"
|
||||
if hasattr(op, "CoolantMode"):
|
||||
opdata = {"opName": opLabel,
|
||||
"coolantMode": op.eCoolantMode}
|
||||
else:
|
||||
opdata = {"opName": opLabel,
|
||||
"coolantMode": "N/A"}
|
||||
data['items'].append(opdata)
|
||||
|
||||
except Exception as e:
|
||||
data['errors'] = e
|
||||
|
||||
return data
|
||||
|
||||
def __fixtureData(self, obj):
|
||||
data = {}
|
||||
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
|
||||
|
||||
return data
|
||||
|
||||
def __outputData(self, obj):
|
||||
data = {}
|
||||
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
|
||||
|
||||
return data
|
||||
|
||||
# def __inspectionData(self, obj):
|
||||
# data = {}
|
||||
# try:
|
||||
# pass
|
||||
|
||||
# except Exception as e:
|
||||
# data['errors'] = e
|
||||
|
||||
# return data
|
||||
|
||||
def __checkTC(self, tc):
|
||||
clean = True
|
||||
if tc.ToolNumber == 0:
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(tc.Label) + " is using ID 0 which the undefined default. Please set a real tool.")+"\n")
|
||||
clean = False
|
||||
if tc.HorizFeed == 0:
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(tc.Label) + " has a 0 value for the Horizontal feed rate")+"\n")
|
||||
clean = False
|
||||
if tc.VertFeed == 0:
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(tc.Label) + " has a 0 value for the Vertical feed rate")+"\n")
|
||||
clean = False
|
||||
if tc.SpindleSpeed == 0:
|
||||
FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(tc.Label) + " has a 0 value for the spindle speed")+"\n")
|
||||
clean = False
|
||||
return clean
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
# register the FreeCAD command
|
||||
FreeCADGui.addCommand('Path_Sanity',CommandPathSanity())
|
||||
FreeCADGui.addCommand('Path_Sanity', CommandPathSanity())
|
||||
|
||||
Reference in New Issue
Block a user