| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | from Path.Post.Processor import PostProcessorFactory |
| | from PySide import QtCore |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| | import FreeCAD |
| | import Path |
| | import Path.Base.SetupSheet as PathSetupSheet |
| | import Path.Base.Util as PathUtil |
| | import Path.Main.Stock as PathStock |
| | import Path.Tool.Controller as PathToolController |
| | import json |
| | import time |
| |
|
| |
|
| | |
| | from lazy_loader.lazy_loader import LazyLoader |
| |
|
| | Draft = LazyLoader("Draft", globals(), "Draft") |
| |
|
| |
|
| | if False: |
| | Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule()) |
| | Path.Log.trackModule(Path.Log.thisModule()) |
| | else: |
| | Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) |
| |
|
| | translate = FreeCAD.Qt.translate |
| |
|
| |
|
| | class JobTemplate: |
| | """Attribute and sub element strings for template export/import.""" |
| |
|
| | Description = "Desc" |
| | GeometryTolerance = "Tolerance" |
| | Job = "Job" |
| | PostProcessor = "Post" |
| | PostProcessorArgs = "PostArgs" |
| | PostProcessorOutputFile = "Output" |
| | Fixtures = "Fixtures" |
| | OrderOutputBy = "OrderOutputBy" |
| | SplitOutput = "SplitOutput" |
| | SetupSheet = "SetupSheet" |
| | Stock = "Stock" |
| | |
| | ToolController = "ToolController" |
| | Version = "Version" |
| |
|
| |
|
| | def isResourceClone(obj, propLink, resourceName): |
| | if hasattr(propLink, "PathResource") and ( |
| | resourceName is None or resourceName == propLink.PathResource |
| | ): |
| | return True |
| | return False |
| |
|
| |
|
| | def createResourceClone(obj, orig, name, icon): |
| | clone = Draft.clone(orig) |
| | clone.Label = "%s-%s" % (name, orig.Label) |
| | clone.addProperty("App::PropertyString", "PathResource") |
| | clone.PathResource = name |
| | if clone.ViewObject: |
| | import Path.Base.Gui.IconViewProvider |
| |
|
| | Path.Base.Gui.IconViewProvider.Attach(clone.ViewObject, icon) |
| | clone.ViewObject.Visibility = False |
| | clone.ViewObject.Transparency = 80 |
| | obj.Document.recompute() |
| | return clone |
| |
|
| |
|
| | def createModelResourceClone(obj, orig): |
| | return createResourceClone(obj, orig, "Model", "BaseGeometry") |
| |
|
| |
|
| | class NotificationClass(QtCore.QObject): |
| | updateTC = QtCore.Signal(object, object) |
| |
|
| |
|
| | Notification = NotificationClass() |
| |
|
| |
|
| | class ObjectJob: |
| | def __init__(self, obj, models, templateFile=None): |
| | self.obj = obj |
| | self.tooltip = None |
| | self.tooltipArgs = None |
| | obj.Proxy = self |
| |
|
| | obj.addProperty( |
| | "App::PropertyFile", |
| | "PostProcessorOutputFile", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "The G-code output file for this project"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "PostProcessor", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "Select the Post Processor"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "PostProcessorArgs", |
| | "Output", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Arguments for the Post Processor (specific to the script)", |
| | ), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "LastPostProcessDate", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "Last Time the Job was post processed"), |
| | ) |
| | obj.setEditorMode("LastPostProcessDate", 2) |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "LastPostProcessOutput", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "Last Time the Job was post processed"), |
| | ) |
| | obj.setEditorMode("LastPostProcessOutput", 2) |
| |
|
| | obj.addProperty( |
| | "App::PropertyString", |
| | "Description", |
| | "Path", |
| | QT_TRANSLATE_NOOP("App::Property", "An optional description for this job"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "CycleTime", |
| | "Path", |
| | QT_TRANSLATE_NOOP("App::Property", "Job Cycle Time Estimation"), |
| | ) |
| | obj.setEditorMode("CycleTime", 1) |
| | obj.addProperty( |
| | "App::PropertyDistance", |
| | "GeometryTolerance", |
| | "Geometry", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "For computing Paths; smaller increases accuracy, but slows down computation", |
| | ), |
| | ) |
| |
|
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "Stock", |
| | "Base", |
| | QT_TRANSLATE_NOOP("App::Property", "Solid object to be used as stock."), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "Operations", |
| | "Base", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "Compound path of all operations in the order they are processed.", |
| | ), |
| | ) |
| |
|
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "JobType", |
| | "Base", |
| | QT_TRANSLATE_NOOP("App::Property", "Select the Type of Job"), |
| | ) |
| | obj.setEditorMode("JobType", 2) |
| |
|
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "SplitOutput", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "Split output into multiple G-code files"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "OrderOutputBy", |
| | "WCS", |
| | QT_TRANSLATE_NOOP("App::Property", "If multiple WCS, order the output this way"), |
| | ) |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Fixtures", |
| | "WCS", |
| | QT_TRANSLATE_NOOP("App::Property", "The Work Coordinate Systems for the Job"), |
| | ) |
| |
|
| | obj.Fixtures = ["G54"] |
| |
|
| | for n in self.propertyEnumerations(): |
| | setattr(obj, n[0], n[1]) |
| |
|
| | obj.PostProcessorOutputFile = Path.Preferences.defaultOutputFile() |
| | postProcessors = Path.Preferences.allEnabledPostProcessors() |
| | |
| | if "" not in postProcessors: |
| | postProcessors = [""] + postProcessors |
| | obj.PostProcessor = postProcessors |
| | defaultPostProcessor = Path.Preferences.defaultPostProcessor() |
| | |
| | if defaultPostProcessor in postProcessors: |
| | obj.PostProcessor = defaultPostProcessor |
| | else: |
| | obj.PostProcessor = "" |
| | obj.PostProcessorArgs = Path.Preferences.defaultPostProcessorArgs() |
| | obj.GeometryTolerance = Path.Preferences.defaultGeometryTolerance() |
| |
|
| | self.setupOperations(obj) |
| | self.setupSetupSheet(obj) |
| | self.setupBaseModel(obj, models) |
| | self.setupToolTable(obj) |
| | self.setFromTemplateFile(obj, templateFile) |
| | self.setupStock(obj) |
| |
|
| | @classmethod |
| | def propertyEnumerations(self, dataType="data"): |
| | """propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. |
| | Args: |
| | dataType = 'data', 'raw', 'translated' |
| | Notes: |
| | 'data' is list of internal string literals used in code |
| | 'raw' is list of (translated_text, data_string) tuples |
| | 'translated' is list of translated string literals |
| | """ |
| |
|
| | enums = { |
| | "OrderOutputBy": [ |
| | (translate("CAM_Job", "Fixture"), "Fixture"), |
| | (translate("CAM_Job", "Tool"), "Tool"), |
| | (translate("CAM_Job", "Operation"), "Operation"), |
| | ], |
| | "JobType": [ |
| | (translate("CAM_Job", "2D"), "2D"), |
| | (translate("CAM_Job", "2.5D"), "2.5D"), |
| | (translate("CAM_Job", "Lathe"), "Lathe"), |
| | (translate("CAM_Job", "Multiaxis"), "Multiaxis"), |
| | ], |
| | } |
| |
|
| | if dataType == "raw": |
| | return enums |
| |
|
| | data = list() |
| | idx = 0 if dataType == "translated" else 1 |
| |
|
| | Path.Log.debug(enums) |
| |
|
| | for k, v in enumerate(enums): |
| | data.append((v, [tup[idx] for tup in enums[v]])) |
| | Path.Log.debug(data) |
| |
|
| | return data |
| |
|
| | def setupOperations(self, obj): |
| | """setupOperations(obj)... setup the Operations group for the Job object.""" |
| | |
| | |
| | |
| | ops = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Operations") |
| | if ops.ViewObject: |
| | |
| | ops.ViewObject.Visibility = True |
| |
|
| | obj.Operations = ops |
| | obj.setEditorMode("Operations", 2) |
| | obj.setEditorMode("Placement", 2) |
| |
|
| | def setupSetupSheet(self, obj): |
| | if not getattr(obj, "SetupSheet", None): |
| | if not hasattr(obj, "SetupSheet"): |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "SetupSheet", |
| | "Base", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "SetupSheet holding the settings for this job" |
| | ), |
| | ) |
| | obj.SetupSheet = PathSetupSheet.Create() |
| | if obj.SetupSheet.ViewObject: |
| | import Path.Base.Gui.IconViewProvider |
| |
|
| | Path.Base.Gui.IconViewProvider.Attach(obj.SetupSheet.ViewObject, "SetupSheet") |
| | obj.SetupSheet.Label = "SetupSheet" |
| | self.setupSheet = obj.SetupSheet.Proxy |
| |
|
| | def setupBaseModel(self, obj, models=None): |
| | Path.Log.track(obj.Label, models) |
| | addModels = False |
| |
|
| | if not hasattr(obj, "Model"): |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "Model", |
| | "Base", |
| | QT_TRANSLATE_NOOP("App::Property", "The base objects for all operations"), |
| | ) |
| | addModels = True |
| | elif obj.Model is None: |
| | addModels = True |
| |
|
| | if addModels: |
| | model = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Model") |
| | if model.ViewObject: |
| | model.ViewObject.Visibility = False |
| | if models: |
| | model.addObjects([createModelResourceClone(obj, base) for base in models]) |
| | obj.Model = model |
| | obj.Model.Label = "Model" |
| |
|
| | if hasattr(obj, "Base"): |
| | Path.Log.info("Converting Job.Base to new Job.Model for {}".format(obj.Label)) |
| | obj.Model.addObject(obj.Base) |
| | obj.Base = None |
| | obj.removeProperty("Base") |
| |
|
| | def setupToolTable(self, obj): |
| | addTable = False |
| | if not hasattr(obj, "Tools"): |
| | obj.addProperty( |
| | "App::PropertyLink", |
| | "Tools", |
| | "Base", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", "Collection of all tool controllers for the job" |
| | ), |
| | ) |
| | addTable = True |
| | elif obj.Tools is None: |
| | addTable = True |
| |
|
| | if addTable: |
| | toolTable = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Tools") |
| | toolTable.Label = "Tools" |
| | if toolTable.ViewObject: |
| | toolTable.ViewObject.Visibility = False |
| | if hasattr(obj, "ToolController"): |
| | toolTable.addObjects(obj.ToolController) |
| | obj.removeProperty("ToolController") |
| | obj.Tools = toolTable |
| |
|
| | def setupStock(self, obj): |
| | """setupStock(obj)... setup the Stock for the Job object.""" |
| | if not obj.Stock: |
| | stockTemplate = Path.Preferences.defaultStockTemplate() |
| | if stockTemplate: |
| | obj.Stock = PathStock.CreateFromTemplate(obj, json.loads(stockTemplate)) |
| | if not obj.Stock: |
| | obj.Stock = PathStock.CreateFromBase(obj) |
| | if obj.Stock.ViewObject: |
| | obj.Stock.ViewObject.Visibility = False |
| |
|
| | def removeBase(self, obj, base, removeFromModel): |
| | if isResourceClone(obj, base, None): |
| | PathUtil.clearExpressionEngine(base) |
| | if removeFromModel: |
| | obj.Model.removeObject(base) |
| | obj.Document.removeObject(base.Name) |
| |
|
| | def modelBoundBox(self, obj): |
| | return PathStock.shapeBoundBox(obj.Model.Group) |
| |
|
| | def onDelete(self, obj, arg2=None): |
| | """Called by the view provider, there doesn't seem to be a callback on the obj itself.""" |
| | Path.Log.track(obj.Label, arg2) |
| | doc = obj.Document |
| |
|
| | if getattr(obj, "Operations", None): |
| | |
| | Path.Log.debug("taking down ops: %s" % [o.Name for o in self.allOperations()]) |
| | while obj.Operations.Group: |
| | op = obj.Operations.Group[0] |
| | if ( |
| | not op.ViewObject |
| | or not hasattr(op.ViewObject.Proxy, "onDelete") |
| | or op.ViewObject.Proxy.onDelete(op.ViewObject, ()) |
| | ): |
| | PathUtil.clearExpressionEngine(op) |
| | doc.removeObject(op.Name) |
| | obj.Operations.Group = [] |
| | doc.removeObject(obj.Operations.Name) |
| | obj.Operations = None |
| |
|
| | |
| | if getattr(obj, "Stock", None): |
| | Path.Log.debug("taking down stock") |
| | PathUtil.clearExpressionEngine(obj.Stock) |
| | doc.removeObject(obj.Stock.Name) |
| | obj.Stock = None |
| |
|
| | |
| | if getattr(obj, "Model", None): |
| | for base in obj.Model.Group: |
| | Path.Log.debug("taking down base %s" % base.Label) |
| | self.removeBase(obj, base, False) |
| | obj.Model.Group = [] |
| | doc.removeObject(obj.Model.Name) |
| | obj.Model = None |
| |
|
| | |
| | if getattr(obj, "Tools", None): |
| | Path.Log.debug("taking down tool controller") |
| | for tc in obj.Tools.Group: |
| | if hasattr(tc.Tool, "BitBody") and tc.Tool.BitBody: |
| | tc.Tool.BitBody.removeObjectsFromDocument() |
| | doc.removeObject(tc.Tool.BitBody.Name) |
| | if hasattr(tc.Tool, "Proxy"): |
| | PathUtil.clearExpressionEngine(tc.Tool) |
| | doc.removeObject(tc.Tool.Name) |
| | PathUtil.clearExpressionEngine(tc) |
| | tc.Proxy.onDelete(tc) |
| | doc.removeObject(tc.Name) |
| | obj.Tools.Group = [] |
| | doc.removeObject(obj.Tools.Name) |
| | obj.Tools = None |
| |
|
| | |
| | if getattr(obj, "SetupSheet", None): |
| | PathUtil.clearExpressionEngine(obj.SetupSheet) |
| | doc.removeObject(obj.SetupSheet.Name) |
| | obj.SetupSheet = None |
| |
|
| | return True |
| |
|
| | def fixupOperations(self, obj): |
| | if getattr(obj.Operations, "ViewObject", None): |
| | try: |
| | obj.Operations.ViewObject.DisplayMode |
| | except Exception: |
| | name = obj.Operations.Name |
| | label = obj.Operations.Label |
| | ops = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython", "Operations") |
| | ops.ViewObject.Proxy = 0 |
| | ops.Group = obj.Operations.Group |
| | obj.Operations.Group = [] |
| | obj.Operations = ops |
| | FreeCAD.ActiveDocument.removeObject(name) |
| | if label == "Unnamed": |
| | ops.Label = "Operations" |
| | else: |
| | ops.Label = label |
| |
|
| | def onDocumentRestored(self, obj): |
| | self.setupBaseModel(obj) |
| | self.fixupOperations(obj) |
| | self.setupSetupSheet(obj) |
| | self.setupToolTable(obj) |
| | self.integrityCheck(obj) |
| |
|
| | obj.setEditorMode("Operations", 2) |
| | obj.setEditorMode("Placement", 2) |
| |
|
| | if hasattr(obj, "Path"): |
| | obj.Path = Path.Path() |
| |
|
| | if not hasattr(obj, "CycleTime"): |
| | obj.addProperty( |
| | "App::PropertyString", |
| | "CycleTime", |
| | "Path", |
| | QT_TRANSLATE_NOOP("App::Property", "Operations Cycle Time Estimation"), |
| | ) |
| | obj.setEditorMode("CycleTime", 1) |
| |
|
| | if not hasattr(obj, "Fixtures"): |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Fixtures", |
| | "WCS", |
| | QT_TRANSLATE_NOOP("App::Property", "The Work Coordinate Systems for the Job"), |
| | ) |
| | obj.Fixtures = ["G54"] |
| |
|
| | if not hasattr(obj, "OrderOutputBy"): |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "OrderOutputBy", |
| | "WCS", |
| | QT_TRANSLATE_NOOP("App::Property", "If multiple WCS, order the output this way"), |
| | ) |
| | obj.OrderOutputBy = ["Fixture", "Tool", "Operation"] |
| |
|
| | if not hasattr(obj, "SplitOutput"): |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "SplitOutput", |
| | "Output", |
| | QT_TRANSLATE_NOOP("App::Property", "Split output into multiple G-code files"), |
| | ) |
| | obj.SplitOutput = False |
| |
|
| | if not hasattr(obj, "JobType"): |
| | obj.addProperty( |
| | "App::PropertyEnumeration", |
| | "JobType", |
| | "Base", |
| | QT_TRANSLATE_NOOP("App::Property", "Select the type of Job"), |
| | ) |
| | obj.setEditorMode("JobType", 2) |
| |
|
| | for n in self.propertyEnumerations(): |
| | setattr(obj, n[0], n[1]) |
| |
|
| | def onChanged(self, obj, prop): |
| | if prop == "PostProcessor" and obj.PostProcessor: |
| | processor = PostProcessorFactory.get_post_processor(obj, obj.PostProcessor) |
| | self.tooltip = processor.tooltip |
| | self.tooltipArgs = processor.tooltipArgs |
| |
|
| | def baseObject(self, obj, base): |
| | """Return the base object, not its clone.""" |
| | if isResourceClone(obj, base, "Model") or isResourceClone(obj, base, "Base"): |
| | if hasattr(base, "Objects") and base.Objects: |
| | return base.Objects[0] |
| | return base |
| |
|
| | def baseObjects(self, obj): |
| | """Return the base objects, not their clones.""" |
| | return [self.baseObject(obj, base) for base in obj.Model.Group] |
| |
|
| | def resourceClone(self, obj, base): |
| | """resourceClone(obj, base) ... Return the resource clone for base if it exists.""" |
| | if isResourceClone(obj, base, None): |
| | return base |
| | for b in obj.Model.Group: |
| | if base == b.Objects[0]: |
| | return b |
| | return None |
| |
|
| | def setFromTemplateFile(self, obj, template): |
| | """setFromTemplateFile(obj, template) ... extract the properties from the given template file and assign to receiver. |
| | This will also create any TCs stored in the template.""" |
| | tcs = [] |
| | if template: |
| | with open(str(template), "rb") as fp: |
| | attrs = json.load(fp) |
| |
|
| | if attrs.get(JobTemplate.Version) and 1 == int(attrs[JobTemplate.Version]): |
| | attrs = self.setupSheet.decodeTemplateAttributes(attrs) |
| | if attrs.get(JobTemplate.SetupSheet): |
| | self.setupSheet.setFromTemplate(attrs[JobTemplate.SetupSheet]) |
| |
|
| | if attrs.get(JobTemplate.GeometryTolerance): |
| | obj.GeometryTolerance = float(attrs.get(JobTemplate.GeometryTolerance)) |
| | if attrs.get(JobTemplate.PostProcessor): |
| | obj.PostProcessor = attrs.get(JobTemplate.PostProcessor) |
| | if attrs.get(JobTemplate.PostProcessorArgs): |
| | obj.PostProcessorArgs = attrs.get(JobTemplate.PostProcessorArgs) |
| | else: |
| | obj.PostProcessorArgs = "" |
| | if attrs.get(JobTemplate.PostProcessorOutputFile): |
| | obj.PostProcessorOutputFile = attrs.get(JobTemplate.PostProcessorOutputFile) |
| | if attrs.get(JobTemplate.Description): |
| | obj.Description = attrs.get(JobTemplate.Description) |
| |
|
| | if attrs.get(JobTemplate.ToolController): |
| | for tc in attrs.get(JobTemplate.ToolController): |
| | ctrl = PathToolController.FromTemplate(tc) |
| | if ctrl: |
| | tcs.append(ctrl) |
| | else: |
| | Path.Log.debug(f"skipping TC {tc['name']}") |
| | if attrs.get(JobTemplate.Stock): |
| | obj.Stock = PathStock.CreateFromTemplate(obj, attrs.get(JobTemplate.Stock)) |
| |
|
| | if attrs.get(JobTemplate.Fixtures): |
| | obj.Fixtures = [x for y in attrs.get(JobTemplate.Fixtures) for x in y] |
| |
|
| | if attrs.get(JobTemplate.OrderOutputBy): |
| | obj.OrderOutputBy = attrs.get(JobTemplate.OrderOutputBy) |
| |
|
| | if attrs.get(JobTemplate.SplitOutput): |
| | obj.SplitOutput = attrs.get(JobTemplate.SplitOutput) |
| |
|
| | Path.Log.debug("setting tool controllers (%d)" % len(tcs)) |
| | if tcs: |
| | obj.Tools.Group = tcs |
| | else: |
| | Path.Log.error( |
| | "Unsupported PathJob template version {}".format(attrs.get(JobTemplate.Version)) |
| | ) |
| |
|
| | if not tcs: |
| | self.addToolController(PathToolController.Create()) |
| |
|
| | def templateAttrs(self, obj): |
| | """templateAttrs(obj) ... answer a dictionary with all properties of the receiver that should be stored in a template file.""" |
| | attrs = {} |
| | attrs[JobTemplate.Version] = 1 |
| | if obj.PostProcessor: |
| | attrs[JobTemplate.PostProcessor] = obj.PostProcessor |
| | attrs[JobTemplate.PostProcessorArgs] = obj.PostProcessorArgs |
| | attrs[JobTemplate.Fixtures] = [{f: True} for f in obj.Fixtures] |
| | attrs[JobTemplate.OrderOutputBy] = obj.OrderOutputBy |
| | attrs[JobTemplate.SplitOutput] = obj.SplitOutput |
| | if obj.PostProcessorOutputFile: |
| | attrs[JobTemplate.PostProcessorOutputFile] = obj.PostProcessorOutputFile |
| | attrs[JobTemplate.GeometryTolerance] = str(obj.GeometryTolerance.Value) |
| | if obj.Description: |
| | attrs[JobTemplate.Description] = obj.Description |
| | return attrs |
| |
|
| | def dumps(self): |
| | return None |
| |
|
| | def loads(self, state): |
| | for obj in FreeCAD.ActiveDocument.Objects: |
| | if hasattr(obj, "Proxy") and obj.Proxy == self: |
| | self.obj = obj |
| | break |
| | return None |
| |
|
| | def execute(self, obj): |
| | if getattr(obj, "Operations", None): |
| | |
| | self.getCycleTime() |
| | if hasattr(obj, "PathChanged"): |
| | obj.PathChanged = True |
| |
|
| | def getCycleTime(self): |
| | seconds = 0 |
| |
|
| | if len(self.obj.Operations.Group): |
| | for op in self.obj.Operations.Group: |
| |
|
| | |
| | if PathUtil.opProperty(op, "Active") is False: |
| | continue |
| |
|
| | |
| | if PathUtil.opProperty(op, "CycleTime") is None: |
| | continue |
| |
|
| | formattedCycleTime = PathUtil.opProperty(op, "CycleTime") |
| | opCycleTime = 0 |
| | try: |
| | |
| | opCycleTime = sum( |
| | x * int(t) |
| | for x, t in zip([1, 60, 3600], reversed(formattedCycleTime.split(":"))) |
| | ) |
| | except Exception: |
| | continue |
| |
|
| | if opCycleTime > 0: |
| | seconds = seconds + opCycleTime |
| |
|
| | cycleTimeString = time.strftime("%H:%M:%S", time.gmtime(seconds)) |
| | self.obj.CycleTime = cycleTimeString |
| |
|
| | def addOperation(self, op, before=None, removeBefore=False): |
| | group = self.obj.Operations.Group |
| | if op not in group: |
| | if before: |
| | try: |
| | group.insert(group.index(before), op) |
| | if removeBefore: |
| | group.remove(before) |
| | except Exception as e: |
| | Path.Log.error(e) |
| | group.append(op) |
| | else: |
| | group.append(op) |
| | self.obj.Operations.Group = group |
| | |
| |
|
| | def nextToolNumber(self): |
| | |
| | group = self.obj.Tools.Group |
| | if len(group) > 0: |
| | return sorted([t.ToolNumber for t in group])[-1] + 1 |
| | else: |
| | return 1 |
| |
|
| | def addToolController(self, tc): |
| | group = self.obj.Tools.Group |
| | Path.Log.debug("addToolController(%s): %s" % (tc.Label, [t.Label for t in group])) |
| | if tc.Name not in [str(t.Name) for t in group]: |
| | tc.setExpression( |
| | "VertRapid", |
| | "%s.%s" |
| | % ( |
| | self.obj.SetupSheet.Proxy.expressionReference(), |
| | PathSetupSheet.Template.VertRapid, |
| | ), |
| | ) |
| | tc.setExpression( |
| | "HorizRapid", |
| | "%s.%s" |
| | % ( |
| | self.obj.SetupSheet.Proxy.expressionReference(), |
| | PathSetupSheet.Template.HorizRapid, |
| | ), |
| | ) |
| | self.obj.Tools.addObject(tc) |
| | Notification.updateTC.emit(self.obj, tc) |
| |
|
| | def allOperations(self): |
| | ops = [] |
| |
|
| | def collectBaseOps(op): |
| | if hasattr(op, "TypeId"): |
| | if op.TypeId == "Path::FeaturePython": |
| | ops.append(op) |
| | if hasattr(op, "Base"): |
| | collectBaseOps(op.Base) |
| | if op.TypeId == "Path::FeatureCompoundPython": |
| | ops.append(op) |
| | for sub in op.Group: |
| | collectBaseOps(sub) |
| |
|
| | if getattr(self.obj, "Operations", None) and getattr(self.obj.Operations, "Group", None): |
| | for op in self.obj.Operations.Group: |
| | collectBaseOps(op) |
| |
|
| | return ops |
| |
|
| | def setCenterOfRotation(self, center): |
| | if center != self.obj.Path.Center: |
| | self.obj.Path.Center = center |
| | for op in self.allOperations(): |
| | op.Path.Center = center |
| |
|
| | def integrityCheck(self, job): |
| | """integrityCheck(job)... Return True if job has all expected children objects. Attempts to restore any missing children.""" |
| | suffix = "" |
| | if len(job.Name) > 3: |
| | suffix = job.Name[3:] |
| |
|
| | def errorMessage(grp, job): |
| | Path.Log.error("{} corrupt in {} job.".format(grp, job.Name)) |
| |
|
| | if not job.Operations: |
| | self.setupOperations(job) |
| | job.Operations.Label = "Operations" + suffix |
| | if not job.Operations: |
| | errorMessage("Operations", job) |
| | return False |
| | if not job.SetupSheet: |
| | self.setupSetupSheet(job) |
| | job.SetupSheet.Label = "SetupSheet" + suffix |
| | if not job.SetupSheet: |
| | errorMessage("SetupSheet", job) |
| | return False |
| | if not job.Model: |
| | self.setupBaseModel(job) |
| | job.Model.Label = "Model" + suffix |
| | if not job.Model: |
| | errorMessage("Model", job) |
| | return False |
| | if not job.Stock: |
| | self.setupStock(job) |
| | job.Stock.Label = "Stock" + suffix |
| | if not job.Stock: |
| | errorMessage("Stock", job) |
| | return False |
| | if not job.Tools: |
| | self.setupToolTable(job) |
| | job.Tools.Label = "Tools" + suffix |
| | if not job.Tools: |
| | errorMessage("Tools", job) |
| | return False |
| | return True |
| |
|
| | @classmethod |
| | def baseCandidates(cls): |
| | """Answer all objects in the current document which could serve as a Base for a job.""" |
| | return sorted( |
| | [obj for obj in FreeCAD.ActiveDocument.Objects if cls.isBaseCandidate(obj)], |
| | key=lambda o: o.Label, |
| | ) |
| |
|
| | @classmethod |
| | def isBaseCandidate(cls, obj): |
| | """Answer true if the given object can be used as a Base for a job.""" |
| | return PathUtil.isValidBaseObject(obj) |
| |
|
| |
|
| | def Instances(): |
| | """Instances() ... Return all Jobs in the current active document.""" |
| | if FreeCAD.ActiveDocument: |
| | return [ |
| | job |
| | for job in FreeCAD.ActiveDocument.Objects |
| | if hasattr(job, "Proxy") and isinstance(job.Proxy, ObjectJob) |
| | ] |
| | return [] |
| |
|
| |
|
| | 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 isinstance(base[0], str): |
| | models = [] |
| | for baseName in base: |
| | models.append(FreeCAD.ActiveDocument.getObject(baseName)) |
| | else: |
| | models = base |
| | obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) |
| | obj.addExtension("App::GroupExtensionPython") |
| | obj.Proxy = ObjectJob(obj, models, templateFile) |
| | return obj |
| |
|