Skip to content

Commit

Permalink
Merge pull request #27 from AndyEveritt/new-button
Browse files Browse the repository at this point in the history
Add new button to post process milling setups automatically
  • Loading branch information
AndyEveritt authored Jun 30, 2020
2 parents 4c7a767 + 4725866 commit 66808b9
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 13 deletions.
15 changes: 12 additions & 3 deletions ASMBL.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,10 @@ def run(context):
# asmblSetupPanel = create_panel(camWorkspace, AsmblTab, 'Setup')

asmblActionsPanel = create_panel(camWorkspace, AsmblTab, 'Actions')

# asmbl post process button
asmblPostProcessControl = create_button(camWorkspace, AsmblTab, asmblActionsPanel,
'Post Process', Handlers.PostProcessCreatedEventHandler,
'Generate ASMBL Script', Handlers.PostProcessCreatedEventHandler,
tooltip='Generate combined gcode file for ASMBL',
resources='./resources/PostProcess')
asmblPostProcessControl.isPromotedByDefault = True
Expand All @@ -113,8 +115,15 @@ def run(context):
<br>Will not work if there are any more/fewer setups</br>'
asmblPostProcessControl.commandDefinition.toolClipFilename = 'resources/GenerateAsmbl/tooltip.png'


pass
# cam post process button
camPostProcessControl = create_button(camWorkspace, AsmblTab, asmblActionsPanel,
'Post Process CAM', Handlers.PostProcessCamCreatedEventHandler,
tooltip='Generate subtractive gcode file for ASMBL',
resources='./resources/PostProcess')
camPostProcessControl.isPromotedByDefault = False
camPostProcessControl.isPromoted = False
camPostProcessControl.commandDefinition.tooltipDescription = '\
<br>Post process all unsuppressed milling setups for the standalone ASMBL program</br>'

except:
if ui:
Expand Down
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,13 @@ Full setup details for operations can be found here: [CAM Operation Setup](docs/
### Standalone

- Generate and Simulate the full Setup to ensure in looks sensible
- In the `Manufacturing` workspace, `Milling` tab; click `Actions` > `Post Process`
- Select the `asmbl_cam.cps` config from the `post_processors` folder in this repo
- You may need to change the `Configuration Folder`
- Set the `Output folder` to the desired project location
- Click `Post`
- In the `Manufacturing` workspace, `ASMBL` tab; click `Actions` > `Post Process Cam`
- Click `Ok`

Fusion will try to open the generated gcode file in VSCode by default, if you don't have it installed it will prompt you to download it. This is entirely up to you.
>A new temporary file is created for each unsuppressed milling setup, rename or change the folder location of any generated file
you want to keep else it may be deleted/overwritten.

> Planned to be streamlined into a single button in a future update
Fusion will try to open the generated gcode file in VSCode by default, if you don't have it installed it will prompt you to download it. This is entirely up to you.

#### Config

Expand Down
2 changes: 1 addition & 1 deletion src/ASMBL_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def group_cam_segments(self, segments, name, strategy, tool):
cutting_height = cutting_segments[0].height
for cutting_segment in cutting_segments[1:]:
# TODO logic needs fixing to deal with non planar and planar segments in same operation
if cutting_segment.height == cutting_height or cutting_segment.planar is False:
if cutting_segment.height == cutting_height or cutting_segment.planar is False or len(cutting_segment.lines) == 1:
cutting_group.append(cutting_segment)
cutting_height = cutting_segment.height
else:
Expand Down
155 changes: 153 additions & 2 deletions src/fusion_api/Handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ def generateAllTootpaths(ui, cam):
ui.messageBox(message)


def postToolpaths(ui, cam, viewResult):
# Check CAM data exists.
def get_setups(ui, cam):
if not cam:
ui.messageBox('No CAM data exists in the active document.')
return
Expand All @@ -82,6 +81,23 @@ def postToolpaths(ui, cam, viewResult):
# Verify that there are any setups.
setups = [setup for setup in cam.setups if not setup.isSuppressed]

return setups


def remove_old_file(path, file_name):
file_path = os.path.join(path, file_name)
if os.path.exists(file_path):
os.remove(file_path)

# ensure file deleted
while os.path.exists(file_path):
pass


def postToolpaths(ui, cam, viewResult):
# get any unsuppressed setups.
setups = get_setups(ui, cam)

setupsCount = len(setups)
if setupsCount < 2:
ui.messageBox('Missing setups, requires an additive & milling setup to work')
Expand Down Expand Up @@ -135,6 +151,51 @@ def postToolpaths(ui, cam, viewResult):
os.startfile(outputFolder)


def postCamToolpath(ui, cam, setup, units, output_folder, part_name):
setup_operation_type = None
# verify there are operations in setup
if setup.operations.count == 0:
ui.messageBox('No CAM operations exist in {}.'.format(setup.name))
return

try:
setup_operation_type = setup.operationType
except:
pass # there is a bug in Fusion as of writing that means Additive setups don't have an operation type

if setup_operation_type == adsk.cam.OperationTypes.MillingOperation:
# remove old files
programName = part_name + '_' + setup.name
remove_old_file(output_folder, programName)

# get post processor
postConfig = os.path.join(Path(__file__).parents[2], 'post_processors', 'asmbl_cam.cps')

# create the postInput object
postInput = adsk.cam.PostProcessInput.create(programName, postConfig, output_folder, units)

cam.postProcess(setup, postInput)

start = time.time()
file_path = os.path.join(output_folder, programName + '.gcode')
while not os.path.exists(file_path):
if time.time() > start + 10:
ui.messageBox('Posting timed out')
return
pass # wait until files exist

checkpoint = time.time()
while True:
if time.time() > checkpoint + 10:
ui.messageBox('Permission Error timed out')
return
try:
open(file_path)
break
except PermissionError:
continue


# Event handler that reacts when the command definitio is executed which
# results in the command being created and this event being fired.
class PostProcessCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
Expand Down Expand Up @@ -331,3 +392,93 @@ def notify(self, args):
return

ui.messageBox('ASMBL gcode has been successfully created. File saved in \'~/Asmbl/output/\'')


# Event handler that reacts when the command definitio is executed which
# results in the command being created and this event being fired.
class PostProcessCamCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
def __init__(self):
super().__init__()

def notify(self, args):
try:
app = adsk.core.Application.get()
ui = app.userInterface

# Get the command that was created.
cmd = adsk.core.Command.cast(args.command)

# Connect to the execute event.
onExecute = PostProcessCamExecuteHandler()
cmd.execute.add(onExecute)
handlers.append(onExecute)

# Get the CommandInputs collection associated with the command.
inputs = cmd.commandInputs

# Create settings tab inputs
tabCmdInputSettings = inputs.addTabCommandInput('settings', 'Settings')
tabSettingsChildInputs = tabCmdInputSettings.children

# Create bool value input to check whether you should generate toolpaths.
generateToolpathsInput = tabSettingsChildInputs.addBoolValueInput(
'generateToolpaths', 'Generate Toolpaths', True, '', False)
generateToolpathsInput.tooltip = 'Regenerated all toolpaths.'

except:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class PostProcessCamExecuteHandler(adsk.core.CommandEventHandler):
# Event handler for the execute event.
def __init__(self):
super().__init__()

def notify(self, args):
app = adsk.core.Application.get()
ui = app.userInterface

eventArgs = adsk.core.CommandEventArgs.cast(args)

# Get the values from the command inputs.
inputs = eventArgs.command.commandInputs

generateToolpaths = inputs.itemById('generateToolpaths').value

doc = app.activeDocument
products = doc.products
product = products.itemByProductType('CAMProductType')
cam = adsk.cam.CAM.cast(product)

if generateToolpaths:
try:
generateAllTootpaths(ui, cam)
except:
ui.messageBox('Failed generating toolpaths:\n{}'.format(traceback.format_exc()))
return

try:
output_folder = os.path.expanduser('~/Asmbl/output/standalone/')

# get any unsuppressed setups.
setups = get_setups(ui, cam)

# specify the NC file output units
units = adsk.cam.PostOutputUnitOptions.DocumentUnitsOutput

# find part name
part_name = doc.name

for setup in setups:
postCamToolpath(ui, cam, setup, units, output_folder, part_name)

except:
ui.messageBox('Failed posting toolpaths:\n{}'.format(traceback.format_exc()))
return

if (os.name == 'posix'):
os.system('open "%s"' % output_folder)
elif (os.name == 'nt'):
os.startfile(output_folder)

ui.messageBox('Milling Setup Post Processing Complete.\nFile saved in \'{}\''.format(output_folder))

0 comments on commit 66808b9

Please sign in to comment.