1848 lines
68 KiB
Plaintext
1848 lines
68 KiB
Plaintext
/*
|
|
|
|
https://github.com/guffy1234/mpcnc_posts_processor
|
|
|
|
MPCNC posts processor for milling and laser/plasma cutting.
|
|
|
|
*/
|
|
|
|
description = "MPCNC Milling/Laser - Marlin 2.0, Grbl 1.1, RepRap";
|
|
vendor = "flyfisher604";
|
|
vendorUrl = "https://github.com/flyfisher604/mpcnc_post_processor";
|
|
|
|
// Internal properties
|
|
certificationLevel = 2;
|
|
extension = "gcode";
|
|
setCodePage("ascii");
|
|
capabilities = CAPABILITY_MILLING | CAPABILITY_JET;
|
|
|
|
machineMode = undefined; //TYPE_MILLING, TYPE_JET
|
|
|
|
var eFirmware = {
|
|
MARLIN: 0,
|
|
GRBL: 1,
|
|
REPRAP: 2,
|
|
prop: {
|
|
0: {name: "Marlin 2.x", value: 0},
|
|
1: {name: "Grbl 1.1", value: 1},
|
|
2: {name: "RepRap", value: 2}
|
|
}
|
|
};
|
|
|
|
var fw = eFirmware.MARLIN;
|
|
|
|
var eComment = {
|
|
Off: 0,
|
|
Important: 1,
|
|
Info: 2,
|
|
Debug: 3,
|
|
prop: {
|
|
0: {name: "Off", value: 0},
|
|
1: {name: "Important", value: 1},
|
|
2: {name: "Info", value: 2},
|
|
3: {name: "Debug", value: 3}
|
|
}
|
|
};
|
|
|
|
var eCoolant = {
|
|
Off: 0,
|
|
Flood: 1,
|
|
Mist: 2,
|
|
ThroughTool: 3,
|
|
Air: 4,
|
|
AirThroughTool: 5,
|
|
Suction: 6,
|
|
FloodMist: 7,
|
|
FloodThroughTool: 8,
|
|
prop: {
|
|
0: {name: "Off", value: 0},
|
|
1: {name: "Flood", value: 1},
|
|
2: {name: "Mist", value: 2},
|
|
3: {name: "ThroughTool", value: 3},
|
|
4: {name: "Air", value: 4},
|
|
5: {name: "AirThroughTool", value: 5},
|
|
6: {name: "Suction", value: 6},
|
|
7: {name: "Flood and Mist", value: 7},
|
|
8: {name: "Flood and ThroughTool", value: 8},
|
|
}
|
|
};
|
|
|
|
|
|
// user-defined properties
|
|
properties = {
|
|
job0_SelectedFirmware : fw, // Firmware to use in special cases
|
|
job1_SetOriginOnStart: true, // Set current position as 0,0,0 on start (G92)
|
|
job2_ManualSpindlePowerControl: true, // Spindle motor is controlled by manual switch
|
|
job3_CommentLevel: eComment.Info, // The level of comments included
|
|
job4_UseArcs: true, // Produce G2/G3 for arcs
|
|
job5_SequenceNumbers: false, // show sequence numbers
|
|
job6_SequenceNumberStart: 10, // first sequence number
|
|
job7_SequenceNumberIncrement: 1, // increment for sequence numbers
|
|
job8_SeparateWordsWithSpace: true, // specifies that the words should be separated with a white space
|
|
job9_GoOriginOnFinish: true, // Go X0 Y0 current Z at end
|
|
|
|
fr0_TravelSpeedXY: 2500, // High speed for travel movements X & Y (mm/min)
|
|
fr1_TravelSpeedZ: 300, // High speed for travel movements Z (mm/min)
|
|
fr2_EnforceFeedrate: true, // Add feedrate to each movement line
|
|
frA_ScaleFeedrate: false, // Will feedrated be scaled
|
|
frB_MaxCutSpeedXY: 900, // Max speed for cut movements X & Y (mm/min)
|
|
frC_MaxCutSpeedZ: 180, // Max speed for cut movements Z (mm/min)
|
|
frD_MaxCutSpeedXYZ: 1000, // Max feedrate after scaling
|
|
|
|
mapD_RestoreFirstRapids: false, // Map first G01 --> G00
|
|
mapE_RestoreRapids: false, // Map G01 --> G00 for SafeTravelsAboveZ
|
|
mapF_SafeZ: "Retract:15", // G01 mapped to G00 if Z is >= jobSafeZRapid
|
|
mapG_AllowRapidZ: false, // Allow G01 --> G00 for vertical retracts and Z descents above safe
|
|
|
|
toolChange0_Enabled: false, // Enable tool change code (bultin tool change requires LCD display)
|
|
toolChange1_X: 0, // X position for builtin tool change
|
|
toolChange2_Y: 0, // Y position for builtin tool change
|
|
toolChange3_Z: 40, // Z position for builtin tool change
|
|
toolChange4_DisableZStepper: false, // disable Z stepper when change a tool
|
|
|
|
probe1_OnStart: false, // Execute probe gcode to align tool
|
|
probe2_OnToolChange: false, // Z probe after tool change
|
|
probe3_Thickness: 0.8, // plate thickness
|
|
probe4_UseHomeZ: true, // use G28 or G38 for probing
|
|
probe5_G38Target: -10, // probing up to pos
|
|
probe6_G38Speed: 30, // probing with speed
|
|
|
|
gcodeStartFile: "", // File with custom Gcode for header/start (in nc folder)
|
|
gcodeStopFile: "", // File with custom Gcode for footer/end (in nc folder)
|
|
gcodeToolFile: "", // File with custom Gcode for tool change (in nc folder)
|
|
gcodeProbeFile: "", // File with custom Gcode for tool probe (in nc folder)
|
|
|
|
cutter1_OnVaporize: 100, // Percentage of power to turn on the laser/plasma cutter in vaporize mode
|
|
cutter2_OnThrough: 80, // Percentage of power to turn on the laser/plasma cutter in through mode
|
|
cutter3_OnEtch: 40, // Percentage of power to turn on the laser/plasma cutter in etch mode
|
|
cutter4_MarlinMode: 106, // Marlin mode laser/plasma cutter
|
|
cutter5_MarlinPin: 4, // Marlin laser/plasma cutter pin for M42
|
|
cutter6_GrblMode: 4, // GRBL mode laser/plasma cutter
|
|
cutter7_Coolant: eCoolant.Off, // Use this coolant. F360 doesn't define a coolant for cutters
|
|
|
|
cl0_coolantA_Mode: eCoolant.Off, // Enable issuing g-codes for control Coolant channel A
|
|
cl1_coolantB_Mode: eCoolant.Off, // Use issuing g-codes for control Coolant channel B
|
|
cl2_coolantAOn: "M42 P6 S255", // GCode command to turn on Coolant channel A
|
|
cl3_coolantAOff: "M42 P6 S0", // Gcode command to turn off Coolant channel A
|
|
cl4_coolantBOn: "M42 P11 S255", // GCode command to turn on Coolant channel B
|
|
cl5_coolantBOff: "M42 P11 S0", // Gcode command to turn off Coolant channel B
|
|
cl6_cust_coolantAOn: "", // Custom GCode command to turn on Coolant channel A
|
|
cl7_cust_coolantAOff: "", // Custom Gcode command to turn off Coolant channel A
|
|
cl8_cust_coolantBOn: "", // Custom GCode command to turn on Coolant channel B
|
|
cl9_cust_coolantBOff: "", // Custom Gcode command to turn off Coolant channel B
|
|
|
|
DuetMillingMode: "M453 P2 I0 R30000 F200", // GCode command to setup Duet3d milling mode
|
|
DuetLaserMode: "M452 P2 I0 R255 F200", // GCode command to setup Duet3d laser mode
|
|
|
|
};
|
|
|
|
propertyDefinitions = {
|
|
|
|
job0_SelectedFirmware: {
|
|
title: "Job: CNC Firmware", description: "Dialect of GCode to create", group: 1,
|
|
type: "integer", default_mm: eFirmware.MARLIN, default_in: eFirmware.MARLIN,
|
|
values: [
|
|
{ title: eFirmware.prop[eFirmware.MARLIN].name, id: eFirmware.MARLIN },
|
|
{ title: eFirmware.prop[eFirmware.GRBL].name, id: eFirmware.GRBL },
|
|
{ title: eFirmware.prop[eFirmware.REPRAP].name, id: eFirmware.REPRAP },
|
|
]
|
|
},
|
|
|
|
job1_SetOriginOnStart: {
|
|
title: "Job: Zero Starting Location (G92)", description: "On start set the current location as 0,0,0 (G92)", group: 1,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
job2_ManualSpindlePowerControl: {
|
|
title: "Job: Manual Spindle On/Off", description: "Enable to manually turn spindle motor on/off", group: 1,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
job3_CommentLevel: {
|
|
title: "Job: Comment Level", description: "Controls the comments include", group: 1,
|
|
type: "integer", default_mm: eComment.Info, default_in: eComment.Info,
|
|
values: [
|
|
{ title: eComment.prop[eComment.Off].name, id: eComment.Off },
|
|
{ title: eComment.prop[eComment.Important].name, id: eComment.Important },
|
|
{ title: eComment.prop[eComment.Info].name, id: eComment.Info },
|
|
{ title: eComment.prop[eComment.Debug].name, id: eComment.Debug },
|
|
]
|
|
},
|
|
job4_UseArcs: {
|
|
title: "Job: Use Arcs", description: "Use G2/G3 g-codes fo circular movements", group: 1,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
job5_SequenceNumbers: {
|
|
title: "Job: Enable Line #s", description: "Show sequence numbers", group: 1,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
job6_SequenceNumberStart: {
|
|
title: "Job: First Line #", description: "First sequence number", group: 1,
|
|
type: "integer", default_mm: 10, default_in: 10
|
|
},
|
|
job7_SequenceNumberIncrement: {
|
|
title: "Job: Line # Increment", description: "Sequence number increment", group: 1,
|
|
type: "integer", default_mm: 1, default_in: 1
|
|
},
|
|
job8_SeparateWordsWithSpace: {
|
|
title: "Job: Include Whitespace", description: "Includes whitespace seperation between text", group: 1,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
job9_GoOriginOnFinish: {
|
|
title: "Job: At end go to 0,0", description: "Go to X0 Y0 at gcode end, Z remains unchanged", group: 1,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
|
|
fr0_TravelSpeedXY: {
|
|
title: "Feed: Travel speed X/Y", description: "High speed for Rapid movements X & Y (mm/min; in/min)", group: 2,
|
|
type: "spatial", default_mm: 2500, default_in: 100
|
|
},
|
|
fr1_TravelSpeedZ: {
|
|
title: "Feed: Travel Speed Z", description: "High speed for Rapid movements z (mm/min; in/min)", group: 2,
|
|
type: "spatial", default_mm: 300, default_in: 12
|
|
},
|
|
fr2_EnforceFeedrate: {
|
|
title: "Feed: Enforce Feedrate", description: "Add feedrate to each movement g-code", group: 2,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
frA_ScaleFeedrate: {
|
|
title: "Feed: Scale Feedrate", description: "Scale feedrate based on X, Y, Z axis maximums", group: 2,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
frB_MaxCutSpeedXY: {
|
|
title: "Feed: Max XY Cut Speed", description: "Maximum X or Y axis cut speed (mm/min; in/min)", group: 2,
|
|
type: "spatial", default_mm: 900, default_in: 35.43
|
|
},
|
|
frC_MaxCutSpeedZ: {
|
|
title: "Feed: Max Z Cut Speed", description: "Maximum Z axis cut speed (mm/min; in/min)", group: 2,
|
|
type: "spatial", default_mm: 180, default_in: 7.08
|
|
},
|
|
frD_MaxCutSpeedXYZ: {
|
|
title: "Feed: Max Toolpath Speed", description: "Maximum scaled feedrate for toolpath (mm/min; in/min)", group: 2,
|
|
type: "spatial", default_mm: 1000, default_in: 39.37
|
|
},
|
|
|
|
mapD_RestoreFirstRapids: {
|
|
title: "Map: First G1 -> G0 Rapid", description: "Ensure move to start of a cut is with a G0 Rapid", group: 3,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
mapE_RestoreRapids: {
|
|
title: "Map: G1s -> G0 Rapids", description: "Enable to convert G1s to G0 Rapids when safe", group: 3,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
mapF_SafeZ: {
|
|
title: "Map: Safe Z to Rapid", description: "Must be above or equal to this value to map G1s --> G0s; constant or keyword (see docs)", group: 3,
|
|
type: "string", default_mm: "Retract:15", default_in: "Retract:15"
|
|
},
|
|
mapG_AllowRapidZ: {
|
|
title: "Map: Allow Rapid Z", description: "Enable to include vertical retracts and safe descents", group: 3,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
|
|
toolChange0_Enabled: {
|
|
title: "Tool Change: Enable", description: "Include tool change code when tool changes (bultin tool change requires LCD display)", group: 4,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
toolChange1_X: {
|
|
title: "Tool Change: X", description: "X location for tool change", group: 4,
|
|
type: "spatial", default_mm: 0, default_in: 0
|
|
},
|
|
toolChange2_Y: {
|
|
title: "Tool Change: Y", description: "Y location for tool change", group: 4,
|
|
type: "spatial", default_mm: 0, default_in: 0
|
|
},
|
|
toolChange3_Z: {
|
|
title: "Tool Change: Z ", description: "Z location for tool change", group: 4,
|
|
type: "spatial", default_mm: 40, default_in: 1.6
|
|
},
|
|
toolChange4_DisableZStepper: {
|
|
title: "Tool Change: Disable Z stepper", description: "Disable Z stepper after reaching tool change location", group: 4,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
|
|
probe1_OnStart: {
|
|
title: "Probe: On job start", description: "Execute probe gcode on job start", group: 5,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
probe2_OnToolChange: {
|
|
title: "Probe: After Tool Change", description: "After tool change, probe Z at the current location", group: 5,
|
|
type: "boolean", default_mm: false, default_in: false
|
|
},
|
|
probe3_Thickness: {
|
|
title: "Probe: Plate thickness", description: "Plate thickness", group: 5,
|
|
type: "spatial", default_mm: 0.8, default_in: 0.032
|
|
},
|
|
probe4_UseHomeZ: {
|
|
title: "Probe: Use Home Z (G28)", description: "Probe with G28 (Yes) or G38 (No)", group: 5,
|
|
type: "boolean", default_mm: true, default_in: true
|
|
},
|
|
probe5_G38Target: {
|
|
title: "Probe: G38 target", description: "G38 Probing's furthest Z position", group: 5,
|
|
type: "spatial", default_mm: -10, default_in: -0.5
|
|
},
|
|
probe6_G38Speed: {
|
|
title: "Probe: G38 speed", description: "G38 Probing's speed (mm/min; in/min)", group: 5,
|
|
type: "spatial", default_mm: 30, default_in: 1.2
|
|
},
|
|
|
|
cutter1_OnVaporize: {
|
|
title: "Laser: On - Vaporize", description: "Persent of power to turn on the laser/plasma cutter in vaporize mode", group: 6,
|
|
type: "number", default_mm: 100, default_in: 100
|
|
},
|
|
cutter2_OnThrough: {
|
|
title: "Laser: On - Through", description: "Persent of power to turn on the laser/plasma cutter in through mode", group: 6,
|
|
type: "number", default_mm: 80, default_in: 80
|
|
},
|
|
cutter3_OnEtch: {
|
|
title: "Laser: On - Etch", description: "Persent of power to on the laser/plasma cutter in etch mode", group: 6,
|
|
type: "number", default_mm: 40, default_in: 40
|
|
},
|
|
cutter4_MarlinMode: {
|
|
title: "Laser: Marlin/Reprap Mode", description: "Marlin/Reprap mode of the laser/plasma cutter", group: 6,
|
|
type: "integer", default_mm: 106, default_in: 106,
|
|
values: [
|
|
{ title: "Fan - M106 S{PWM}/M107", id: 106 },
|
|
{ title: "Spindle - M3 O{PWM}/M5", id: 3 },
|
|
{ title: "Pin - M42 P{pin} S{PWM}", id: 42 },
|
|
]
|
|
},
|
|
cutter5_MarlinPin: {
|
|
title: "Laser: Marlin M42 Pin", description: "Marlin custom pin number for the laser/plasma cutter", group: 6,
|
|
type: "integer", default_mm: 4, default_in: 4
|
|
},
|
|
cutter6_GrblMode: {
|
|
title: "Laser: GRBL Mode", description: "GRBL mode of the laser/plasma cutter", group: 6,
|
|
type: "integer", default_mm: 4, default_in: 4,
|
|
values: [
|
|
{ title: "M4 S{PWM}/M5 dynamic power", id: 4 },
|
|
{ title: "M3 S{PWM}/M5 static power", id: 3 },
|
|
]
|
|
},
|
|
cutter7_Coolant: {
|
|
title: "Laser: Coolant", description: "Force a coolant to be used", group: 6,
|
|
type: "integer", default_mm: eCoolant.Off, default_in: eCoolant.Off,
|
|
values: [
|
|
{ title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off },
|
|
{ title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood },
|
|
{ title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist },
|
|
{ title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air },
|
|
{ title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction },
|
|
{ title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist },
|
|
{ title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool }
|
|
]
|
|
},
|
|
|
|
gcodeStartFile: {
|
|
title: "Extern: Start File", description: "File with custom Gcode for header/start (in nc folder)", group: 7,
|
|
type: "file", default_mm: "", default_in: ""
|
|
},
|
|
gcodeStopFile: {
|
|
title: "Extern: Stop File", description: "File with custom Gcode for footer/end (in nc folder)", group: 7,
|
|
type: "file", default_mm: "", default_in: ""
|
|
},
|
|
gcodeToolFile: {
|
|
title: "Extern: Tool File", description: "File with custom Gcode for tool change (in nc folder)", group: 7,
|
|
type: "file", default_mm: "", default_in: ""
|
|
},
|
|
gcodeProbeFile: {
|
|
title: "Extern: Probe File", description: "File with custom Gcode for tool probe (in nc folder)", group: 7,
|
|
type: "file", default_mm: "", default_in: ""
|
|
},
|
|
|
|
// Coolant
|
|
cl0_coolantA_Mode: {
|
|
title: "Coolant: A Mode", description: "Enable channel A when tool is set this coolant", group: 8,
|
|
type: "integer", default_mm: 0, default_in: 0,
|
|
values: [
|
|
{ title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off },
|
|
{ title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood },
|
|
{ title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist },
|
|
{ title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air },
|
|
{ title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction },
|
|
{ title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist },
|
|
{ title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool }
|
|
]
|
|
},
|
|
cl1_coolantB_Mode: {
|
|
title: "Coolant: B Mode", description: "Enable channel B when tool is set this coolant", group: 8,
|
|
type: "integer", default_mm: 0, default_in: 0,
|
|
values: [
|
|
{ title: eCoolant.prop[eCoolant.Off].name, id: eCoolant.Off },
|
|
{ title: eCoolant.prop[eCoolant.Flood].name, id: eCoolant.Flood },
|
|
{ title: eCoolant.prop[eCoolant.Mist].name, id: eCoolant.Mist },
|
|
{ title: eCoolant.prop[eCoolant.ThroughTool].name, id: eCoolant.ThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Air].name, id: eCoolant.Air },
|
|
{ title: eCoolant.prop[eCoolant.AirThroughTool].name, id: eCoolant.AirThroughTool },
|
|
{ title: eCoolant.prop[eCoolant.Suction].name, id: eCoolant.Suction },
|
|
{ title: eCoolant.prop[eCoolant.FloodMist].name, id: eCoolant.FloodMist },
|
|
{ title: eCoolant.prop[eCoolant.FloodThroughTool].name, id: eCoolant.FloodThroughTool }
|
|
]
|
|
},
|
|
cl2_coolantAOn: {
|
|
title: "Coolant: A Enable", description: "GCode to turn On coolant channel A", group: 8,
|
|
type: "enum", default_mm: "M42 P6 S255", default_in: "M42 P6 S255",
|
|
values: [
|
|
{ title: "Mrln: M42 P6 S255", id: "M42 P6 S255" },
|
|
{ title: "Mrln: M42 P11 S255", id: "M42 P11 S255" },
|
|
{ title: "Grbl: M7 (mist)", id: "M7" },
|
|
{ title: "Grbl: M8 (flood)", id: "M8" },
|
|
{ title: "Use custom", id: "Use custom" }
|
|
]
|
|
},
|
|
cl3_coolantAOff: {
|
|
title: "Coolant: A Disable", description: "Gcode to turn Off coolant A", group: 8,
|
|
type: "enum", default_mm: "M42 P6 S0", default_in: "M42 P6 S0",
|
|
values: [
|
|
{ title: "Mrln: M42 P6 S0", id: "M42 P6 S0" },
|
|
{ title: "Mrln: M42 P11 S0", id: "M42 P11 S0" },
|
|
{ title: "Grbl: M9 (off)", id: "M9" },
|
|
{ title: "Use custom", id: "Use custom" }
|
|
]
|
|
},
|
|
cl4_coolantBOn: {
|
|
title: "Coolant: B Enable", description: "GCode to turn On coolant channel B", group: 8,
|
|
type: "enum", default_mm: "M42 P11 S255", default_in: "M42 P11 S255",
|
|
values: [
|
|
{ title: "Mrln: M42 P11 S255", id: "M42 P11 S255" },
|
|
{ title: "Mrln: M42 P6 S255", id: "M42 P6 S255" },
|
|
{ title: "Grbl: M7 (mist)", id: "M7" },
|
|
{ title: "Grbl: M8 (flood)", id: "M8" },
|
|
{ title: "Use custom", id: "Use custom" }
|
|
]
|
|
},
|
|
cl5_coolantBOff: {
|
|
title: "Coolant: B Disable", description: "Gcode to turn Off coolant B", group: 8,
|
|
type: "enum", default_mm: "M42 P11 S0", default_in: "M42 P11 S0",
|
|
values: [
|
|
{ title: "Mrln: M42 P11 S0", id: "M42 P11 S0" },
|
|
{ title: "Mrln: M42 P6 S0", id: "M42 P6 S0" },
|
|
{ title: "Grbl: M9 (off)", id: "M9" },
|
|
{ title: "Use custom", id: "Use custom" }
|
|
]
|
|
},
|
|
cl6_cust_coolantAOn: {
|
|
title: "Coolant: Custom A Enable", description: "Custom GCode to turn On coolant channel A", group: 8,
|
|
type: "string", default_mm: "", default_in: "",
|
|
},
|
|
cl7_cust_coolantAOff: {
|
|
title: "Coolant: Custom A Disable", description: "Custom Gcode to turn Off coolant A", group: 8,
|
|
type: "string", default_mm: "", default_in: "",
|
|
},
|
|
cl8_cust_coolantBOn: {
|
|
title: "Coolant: Custom B Enable", description: "Custom GCode to turn On coolant channel B", group: 8,
|
|
type: "string", default_mm: "", default_in: "",
|
|
},
|
|
cl9_cust_coolantBOff: {
|
|
title: "Coolant: Custom B Disable", description: "Custom Gcode to turn Off coolant B", group: 8,
|
|
type: "string", default_mm: "", default_in: "",
|
|
},
|
|
|
|
DuetMillingMode: {
|
|
title: "Duet: Milling mode", description: "GCode command to setup Duet3d milling mode", group: 9,
|
|
type: "string", default_mm: "M453 P2 I0 R30000 F200", default_in: "M453 P2 I0 R30000 F200"
|
|
},
|
|
DuetLaserMode: {
|
|
title: "Duet: Laser mode", description: "GCode command to setup Duet3d laser mode", group: 9,
|
|
type: "string", default_mm: "M452 P2 I0 R255 F200", default_in: "M452 P2 I0 R255 F200"
|
|
},
|
|
};
|
|
|
|
var sequenceNumber;
|
|
|
|
// Formats
|
|
var gFormat = createFormat({ prefix: "G", decimals: 1 });
|
|
var mFormat = createFormat({ prefix: "M", decimals: 0 });
|
|
|
|
var xyzFormat = createFormat({ decimals: (unit == MM ? 3 : 4) });
|
|
var xFormat = createFormat({ prefix: "X", decimals: (unit == MM ? 3 : 4) });
|
|
var yFormat = createFormat({ prefix: "Y", decimals: (unit == MM ? 3 : 4) });
|
|
var zFormat = createFormat({ prefix: "Z", decimals: (unit == MM ? 3 : 4) });
|
|
var iFormat = createFormat({ prefix: "I", decimals: (unit == MM ? 3 : 4) });
|
|
var jFormat = createFormat({ prefix: "J", decimals: (unit == MM ? 3 : 4) });
|
|
var kFormat = createFormat({ prefix: "K", decimals: (unit == MM ? 3 : 4) });
|
|
|
|
var speedFormat = createFormat({ decimals: 0 });
|
|
var sFormat = createFormat({ prefix: "S", decimals: 0 });
|
|
|
|
var pFormat = createFormat({ prefix: "P", decimals: 0 });
|
|
var oFormat = createFormat({ prefix: "O", decimals: 0 });
|
|
|
|
var feedFormat = createFormat({ decimals: (unit == MM ? 0 : 2) });
|
|
var fFormat = createFormat({ prefix: "F", decimals: (unit == MM ? 0 : 2) });
|
|
|
|
var toolFormat = createFormat({ decimals: 0 });
|
|
var tFormat = createFormat({ prefix: "T", decimals: 0 });
|
|
|
|
var taperFormat = createFormat({ decimals: 1, scale: DEG });
|
|
var secFormat = createFormat({ decimals: 3, forceDecimal: true }); // seconds - range 0.001-1000
|
|
|
|
// Linear outputs
|
|
var xOutput = createVariable({}, xFormat);
|
|
var yOutput = createVariable({}, yFormat);
|
|
var zOutput = createVariable({}, zFormat);
|
|
var fOutput = createVariable({ force: false }, fFormat);
|
|
var sOutput = createVariable({ force: true }, sFormat);
|
|
|
|
// Circular outputs
|
|
var iOutput = createReferenceVariable({}, iFormat);
|
|
var jOutput = createReferenceVariable({}, jFormat);
|
|
var kOutput = createReferenceVariable({}, kFormat);
|
|
|
|
// Modals
|
|
var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ...
|
|
var gPlaneModal = createModal({ onchange: function () { gMotionModal.reset(); } }, gFormat); // modal group 2 // G17-19
|
|
var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91
|
|
var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94
|
|
var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21
|
|
|
|
// Arc support variables
|
|
minimumChordLength = spatial(0.01, MM);
|
|
minimumCircularRadius = spatial(0.01, MM);
|
|
maximumCircularRadius = spatial(1000, MM);
|
|
minimumCircularSweep = toRad(0.01);
|
|
maximumCircularSweep = toRad(180);
|
|
allowHelicalMoves = false;
|
|
allowedCircularPlanes = undefined;
|
|
|
|
// Writes the specified block.
|
|
function writeBlock() {
|
|
if (properties.job5_SequenceNumbers) {
|
|
writeWords2("N" + sequenceNumber, arguments);
|
|
sequenceNumber += properties.job7_SequenceNumberIncrement;
|
|
} else {
|
|
writeWords(arguments);
|
|
}
|
|
}
|
|
|
|
function flushMotions() {
|
|
if (fw == eFirmware.GRBL) {
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeBlock(mFormat.format(400));
|
|
}
|
|
}
|
|
|
|
//---------------- Safe Rapids ----------------
|
|
|
|
var eSafeZ = {
|
|
CONST: 0,
|
|
FEED: 1,
|
|
RETRACT: 2,
|
|
CLEARANCE: 3,
|
|
ERROR: 4,
|
|
prop: {
|
|
0: {name: "Const", regex: /^\d+\.?\d*$/, numRegEx: /^(\d+\.?\d*)$/, value: 0},
|
|
1: {name: "Feed", regex: /^Feed:/i, numRegEx: /:(\d+\.?\d*)$/, value: 1},
|
|
2: {name: "Retract", regex: /^Retract:/i, numRegEx: /:(\d+\.?\d*)$/, alue: 2},
|
|
3: {name: "Clearance", regex: /^Clearance:/i, numRegEx: /:(\d+\.?\d*)$/, value: 3},
|
|
4: {name: "Error", regex: /^$/, numRegEx: /^$/, value: 4}
|
|
}
|
|
};
|
|
|
|
var safeZMode = eSafeZ.CONST;
|
|
var safeZHeightDefault = 15;
|
|
var safeZHeight;
|
|
|
|
function parseSafeZProperty() {
|
|
var str = properties.mapF_SafeZ;
|
|
|
|
// Look for either a number by itself or 'Feed:', 'Retract:' or 'Clearance:'
|
|
for (safeZMode = eSafeZ.CONST; safeZMode < eSafeZ.ERROR; safeZMode++) {
|
|
if (str.search(eSafeZ.prop[safeZMode].regex) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If it was not an error then get the number
|
|
if (safeZMode != eSafeZ.ERROR) {
|
|
safeZHeightDefault = str.match(eSafeZ.prop[safeZMode].numRegEx);
|
|
|
|
if ((safeZHeightDefault == null) || (safeZHeightDefault.length !=2)) {
|
|
writeComment(eComment.Debug, " parseSafeZProperty: " + safeZHeightDefault);
|
|
writeComment(eComment.Debug, " parseSafeZProperty.length: " + (safeZHeightDefault != null? safeZHeightDefault.length : "na"));
|
|
writeComment(eComment.Debug, " parseSafeZProperty: Couldn't find number");
|
|
safeZMode = eSafeZ.ERROR;
|
|
safeZHeightDefault = 15;
|
|
}
|
|
else {
|
|
safeZHeightDefault = safeZHeightDefault[1];
|
|
}
|
|
}
|
|
|
|
writeComment(eComment.Debug, " parseSafeZProperty: safeZMode = '" + eSafeZ.prop[safeZMode].name + "'");
|
|
writeComment(eComment.Debug, " parseSafeZProperty: safeZHeightDefault = " + safeZHeightDefault);
|
|
}
|
|
|
|
function safeZforSection(_section)
|
|
{
|
|
if (properties.mapE_RestoreRapids) {
|
|
switch (safeZMode) {
|
|
case eSafeZ.CONST:
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ using const: " + safeZHeight);
|
|
break;
|
|
|
|
case eSafeZ.FEED:
|
|
if (hasParameter("operation:feedHeight_value") && hasParameter("operation:feedHeight_absolute")) {
|
|
let feed = _section.getParameter("operation:feedHeight_value");
|
|
let abs = _section.getParameter("operation:feedHeight_absolute");
|
|
|
|
if (abs == 1) {
|
|
safeZHeight = feed;
|
|
writeComment(eComment.Info, " SafeZ feed level: " + safeZHeight);
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ feed level not abs: " + safeZHeight);
|
|
}
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ feed level not defined: " + safeZHeight);
|
|
}
|
|
break;
|
|
|
|
case eSafeZ.RETRACT:
|
|
if (hasParameter("operation:retractHeight_value") && hasParameter("operation:retractHeight_absolute")) {
|
|
let retract = _section.getParameter("operation:retractHeight_value");
|
|
let abs = _section.getParameter("operation:retractHeight_absolute");
|
|
|
|
if (abs == 1) {
|
|
safeZHeight = retract;
|
|
writeComment(eComment.Info, " SafeZ retract level: " + safeZHeight);
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ retract level not abs: " + safeZHeight);
|
|
}
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ: retract level not defined: " + safeZHeight);
|
|
}
|
|
break;
|
|
|
|
case eSafeZ.CLEARANCE:
|
|
if (hasParameter("operation:clearanceHeight_value") && hasParameter("operation:clearanceHeight_absolute")) {
|
|
var clearance = _section.getParameter("operation:clearanceHeight_value");
|
|
let abs = _section.getParameter("operation:clearanceHeight_absolute");
|
|
|
|
if (abs == 1) {
|
|
safeZHeight = clearance;
|
|
writeComment(eComment.Info, " SafeZ clearance level: " + safeZHeight);
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ clearance level not abs: " + safeZHeight);
|
|
}
|
|
}
|
|
else {
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " SafeZ clearance level not defined: " + safeZHeight);
|
|
}
|
|
break;
|
|
|
|
case eSafeZ.ERROR:
|
|
safeZHeight = safeZHeightDefault;
|
|
writeComment(eComment.Important, " >>> WARNING: " + propertyDefinitions.mapF_SafeZ.title + "format error: " + safeZHeight);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Number.prototype.round = function(places) {
|
|
return +(Math.round(this + "e+" + places) + "e-" + places);
|
|
}
|
|
|
|
// Returns true if the rules to convert G1s to G0s are satisfied
|
|
function isSafeToRapid(x, y, z) {
|
|
if (properties.mapE_RestoreRapids) {
|
|
|
|
// Calculat a z to 3 decimal places for zSafe comparison, every where else use z to avoid mixing rounded with unrounded
|
|
var z_round = z.round(3);
|
|
writeComment(eComment.Debug, "isSafeToRapid z: " + z + " z_round: " + z_round);
|
|
|
|
let zSafe = (z_round >= safeZHeight);
|
|
|
|
writeComment(eComment.Debug, "isSafeToRapid zSafe: " + zSafe + " z_round: " + z_round + " safeZHeight: " + safeZHeight);
|
|
|
|
// Destination z must be in safe zone.
|
|
if (zSafe) {
|
|
let cur = getCurrentPosition();
|
|
let zConstant = (z == cur.z);
|
|
let zUp = (z > cur.z);
|
|
let xyConstant = ((x == cur.x) && (y == cur.y));
|
|
let curZSafe = (cur.z >= safeZHeight);
|
|
writeComment(eComment.Debug, "isSafeToRapid curZSafe: " + curZSafe + " cur.z: " + cur.z);
|
|
|
|
// Restore Rapids only when the target Z is safe and
|
|
// Case 1: Z is not changing, but XY are
|
|
// Case 2: Z is increasing, but XY constant
|
|
|
|
// Z is not changing and we know we are in the safe zone
|
|
if (zConstant) {
|
|
return true;
|
|
}
|
|
|
|
// We include moves of Z up as long as xy are constant
|
|
else if (properties.mapG_AllowRapidZ && zUp && xyConstant) {
|
|
return true;
|
|
}
|
|
|
|
// We include moves of Z down as long as xy are constant and z always remains safe
|
|
else if (properties.mapG_AllowRapidZ && (!zUp) && xyConstant && curZSafe) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//---------------- Coolant ----------------
|
|
|
|
function CoolantA(on) {
|
|
var coolantText = on ? properties.cl2_coolantAOn : properties.cl3_coolantAOff;
|
|
|
|
if (coolantText == "Use custom") {
|
|
coolantText = on ? properties.cl6_cust_coolantAOn : properties.cl7_cust_coolantAOff;
|
|
}
|
|
|
|
writeBlock(coolantText);
|
|
}
|
|
|
|
function CoolantB(on) {
|
|
var coolantText = on ? properties.cl4_coolantBOn : properties.cl5_coolantBOff;
|
|
|
|
if (coolantText == "Use custom") {
|
|
coolantText = on ? properties.cl8_cust_coolantBOn : properties.cl9_cust_coolantBOff;
|
|
}
|
|
|
|
writeBlock(coolantText);
|
|
}
|
|
|
|
// Manage two channels of coolant by tracking which coolant is being using for
|
|
// a channel (0 = disabled). SetCoolant called with desired coolant to use or 0 to disable
|
|
|
|
var curCoolant = eCoolant.Off; // The coolant requested by the tool
|
|
var coolantChannelA = eCoolant.Off; // The coolant running in ChannelA
|
|
var coolantChannelB = eCoolant.Off; // The coolant running in ChannelB
|
|
|
|
function setCoolant(coolant) {
|
|
writeComment(eComment.Debug, " ---- Coolant: " + coolant + " cur: " + curCoolant + " A: " + coolantChannelA + " B: " + coolantChannelB);
|
|
|
|
// If the coolant for this tool is the same as the current coolant then there is nothing to do
|
|
if (curCoolant == coolant) {
|
|
return;
|
|
}
|
|
|
|
// We are changing coolant, so disable any active coolant channels
|
|
// before we switch to the other coolant
|
|
if (coolantChannelA != eCoolant.Off) {
|
|
writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel A: " + eCoolant.prop[eCoolant.Off].name);
|
|
coolantChannelA = eCoolant.Off;
|
|
CoolantA(false);
|
|
}
|
|
|
|
if (coolantChannelB != eCoolant.Off) {
|
|
writeComment((coolant == eCoolant.Off) ? eComment.Important: eComment.Info, " >>> Coolant Channel B: " + eCoolant.prop[eCoolant.Off].name);
|
|
coolantChannelB = eCoolant.Off;
|
|
CoolantB(false);
|
|
}
|
|
|
|
// At this point we know that all coolant is off so make that the current coolant
|
|
curCoolant = eCoolant.Off;
|
|
|
|
// As long as we are not disabling coolant (coolant = 0), then check if either coolant channel
|
|
// matches the coolant requested. If neither do then issue an warning
|
|
|
|
var warn = true;
|
|
|
|
if (coolant != eCoolant.Off) {
|
|
if (properties.cl0_coolantA_Mode == coolant) {
|
|
writeComment(eComment.Important, " >>> Coolant Channel A: " + eCoolant.prop[coolant].name);
|
|
coolantChannelA = coolant;
|
|
curCoolant = coolant;
|
|
warn = false;
|
|
CoolantA(true);
|
|
}
|
|
|
|
if (properties.cl1_coolantB_Mode == coolant) {
|
|
writeComment(eComment.Important, " >>> Coolant Channel B: " + eCoolant.prop[coolant].name);
|
|
coolantChannelB = coolant;
|
|
curCoolant = coolant;
|
|
warn = false;
|
|
CoolantB(true);
|
|
}
|
|
|
|
if (warn) {
|
|
writeComment(eComment.Important, " >>> WARNING: No matching Coolant channel : " + ((coolant <= eCoolant.FloodThroughTool) ? eCoolant.prop[coolant].name : "unknown") + " requested");
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------- Cutters - Waterjet/Laser/Plasma ----------------
|
|
|
|
var cutterOnCurrentPower;
|
|
|
|
function laserOn(power) {
|
|
// Firmware is Grbl
|
|
if (fw == eFirmware.GRBL) {
|
|
var laser_pwm = power * 10;
|
|
|
|
writeBlock(mFormat.format(properties.cutter6_GrblMode), sFormat.format(laser_pwm));
|
|
}
|
|
|
|
// Default firmware
|
|
else {
|
|
var laser_pwm = power / 100 * 255;
|
|
|
|
switch (properties.cutter4_MarlinMode) {
|
|
case 106:
|
|
writeBlock(mFormat.format(106), sFormat.format(laser_pwm));
|
|
break;
|
|
case 3:
|
|
if (fw == eFirmware.REPRAP) {
|
|
writeBlock(mFormat.format(3), sFormat.format(laser_pwm));
|
|
} else {
|
|
writeBlock(mFormat.format(3), oFormat.format(laser_pwm));
|
|
}
|
|
break;
|
|
case 42:
|
|
writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(laser_pwm));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function laserOff() {
|
|
// Firmware is Grbl
|
|
if (fw == eFirmware.GRBL) {
|
|
writeBlock(mFormat.format(5));
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
switch (properties.cutter4_MarlinMode) {
|
|
case 106:
|
|
writeBlock(mFormat.format(107));
|
|
break;
|
|
case 3:
|
|
writeBlock(mFormat.format(5));
|
|
break;
|
|
case 42:
|
|
writeBlock(mFormat.format(42), pFormat.format(properties.cutter5_MarlinPin), sFormat.format(0));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------- on Entry Points ----------------
|
|
|
|
// Called in every new gcode file
|
|
function onOpen() {
|
|
fw = properties.job0_SelectedFirmware;
|
|
|
|
// Output anything special to start the GCode
|
|
if (fw == eFirmware.GRBL) {
|
|
writeln("%");
|
|
}
|
|
|
|
// Configure the GCode G commands
|
|
if (fw == eFirmware.GRBL) {
|
|
gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ...
|
|
}
|
|
else {
|
|
gMotionModal = createModal({ force: true }, gFormat); // modal group 1 // G0-G3, ...
|
|
}
|
|
|
|
// Configure how the feedrate is formatted
|
|
if (properties.fr2_EnforceFeedrate) {
|
|
fOutput = createVariable({ force: true }, fFormat);
|
|
}
|
|
|
|
// Set the starting sequence number for line numbering
|
|
sequenceNumber = properties.job6_SequenceNumberStart;
|
|
|
|
// Set the seperator used between text
|
|
if (!properties.job8_SeparateWordsWithSpace) {
|
|
setWordSeparator("");
|
|
}
|
|
|
|
// Determine the safeZHeight to do rapids
|
|
parseSafeZProperty();
|
|
}
|
|
|
|
// Called at end of gcode file
|
|
function onClose() {
|
|
writeComment(eComment.Important, " *** STOP begin ***");
|
|
|
|
flushMotions();
|
|
|
|
if (properties.gcodeStopFile == "") {
|
|
onCommand(COMMAND_COOLANT_OFF);
|
|
if (properties.job9_GoOriginOnFinish) {
|
|
rapidMovementsXY(0, 0);
|
|
}
|
|
onCommand(COMMAND_STOP_SPINDLE);
|
|
|
|
end(true);
|
|
|
|
writeComment(eComment.Important, " *** STOP end ***");
|
|
} else {
|
|
loadFile(properties.gcodeStopFile);
|
|
}
|
|
|
|
if (fw == eFirmware.GRBL) {
|
|
writeln("%");
|
|
}
|
|
}
|
|
|
|
var forceSectionToStartWithRapid = false;
|
|
|
|
function onSection() {
|
|
// Every section needs to start with a Rapid to get to the initial location.
|
|
// In the hobby version Rapids have been elliminated and the first command is
|
|
// a onLinear not a onRapid command. This results in not current position being
|
|
// that same as the cut to position which means wecan't determine the direction
|
|
// of the move. Without a direction vector we can't scale the feedrate or convert
|
|
// onLinear moves back into onRapids. By ensuring the first onLinear is treated as
|
|
// a onRapid we have a currentPosition that is correct.
|
|
|
|
forceSectionToStartWithRapid = true;
|
|
|
|
// Write Start gcode of the documment (after the "onParameters" with the global info)
|
|
if (isFirstSection()) {
|
|
writeFirstSection();
|
|
}
|
|
|
|
writeComment(eComment.Important, " *** SECTION begin ***");
|
|
|
|
// Print min/max boundaries for each section
|
|
vectorX = new Vector(1, 0, 0);
|
|
vectorY = new Vector(0, 1, 0);
|
|
writeComment(eComment.Info, " X Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMinimum()) + " - X Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorX).getMaximum()));
|
|
writeComment(eComment.Info, " Y Min: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMinimum()) + " - Y Max: " + xyzFormat.format(currentSection.getGlobalRange(vectorY).getMaximum()));
|
|
writeComment(eComment.Info, " Z Min: " + xyzFormat.format(currentSection.getGlobalZRange().getMinimum()) + " - Z Max: " + xyzFormat.format(currentSection.getGlobalZRange().getMaximum()));
|
|
|
|
// Determine the Safe Z Height to map G1s to G0s
|
|
safeZforSection(currentSection);
|
|
|
|
// Do a tool change if tool changes are enabled and its not the first section and this section uses
|
|
// a different tool then the previous section
|
|
if (properties.toolChange0_Enabled && !isFirstSection() && tool.number != getPreviousSection().getTool().number) {
|
|
if (properties.gcodeToolFile == "") {
|
|
// Post Processor does the tool change
|
|
|
|
writeComment(eComment.Important, " --- Tool Change Start")
|
|
toolChange();
|
|
writeComment(eComment.Important, " --- Tool Change End")
|
|
} else {
|
|
// Users custom tool change gcode is used
|
|
loadFile(properties.gcodeToolFile);
|
|
}
|
|
}
|
|
|
|
// Machining type
|
|
if (currentSection.type == TYPE_MILLING) {
|
|
// Specific milling code
|
|
writeComment(eComment.Info, " " + sectionComment + " - Milling - Tool: " + tool.number + " - " + tool.comment + " " + getToolTypeName(tool.type));
|
|
}
|
|
|
|
else if (currentSection.type == TYPE_JET) {
|
|
var jetModeStr;
|
|
var warn = false;
|
|
|
|
// Cutter mode used for different cutting power in PWM laser
|
|
switch (currentSection.jetMode) {
|
|
case JET_MODE_THROUGH:
|
|
cutterOnCurrentPower = properties.cutter2_OnThrough;
|
|
jetModeStr = "Through"
|
|
break;
|
|
case JET_MODE_ETCHING:
|
|
cutterOnCurrentPower = properties.cutter3_OnEtch;
|
|
jetModeStr = "Etching"
|
|
break;
|
|
case JET_MODE_VAPORIZE:
|
|
jetModeStr = "Vaporize"
|
|
cutterOnCurrentPower = properties.cutter1_OnVaporize;
|
|
break;
|
|
default:
|
|
jetModeStr = "*** Unknown ***"
|
|
warn = true;
|
|
}
|
|
|
|
if (warn) {
|
|
writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr);
|
|
writeComment(eComment.Important, "Selected cutting mode " + currentSection.jetMode + " not mapped to power level");
|
|
}
|
|
else {
|
|
writeComment(eComment.Info, " " + sectionComment + ", Laser/Plasma Cutting mode: " + getParameter("operation:cuttingMode") + ", jetMode: " + jetModeStr + ", power: " + cutterOnCurrentPower);
|
|
}
|
|
}
|
|
|
|
// Adjust the mode
|
|
if (fw == eFirmware.REPRAP) {
|
|
if (machineMode != currentSection.type) {
|
|
switch (currentSection.type) {
|
|
case TYPE_MILLING:
|
|
writeBlock(properties.DuetMillingMode);
|
|
break;
|
|
case TYPE_JET:
|
|
writeBlock(properties.DuetLaserMode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
machineMode = currentSection.type;
|
|
|
|
onCommand(COMMAND_START_SPINDLE);
|
|
onCommand(COMMAND_COOLANT_ON);
|
|
|
|
// Display section name in LCD
|
|
display_text(" " + sectionComment);
|
|
}
|
|
|
|
// Called in every section end
|
|
function onSectionEnd() {
|
|
resetAll();
|
|
writeComment(eComment.Important, " *** SECTION end ***");
|
|
writeComment(eComment.Important, "");
|
|
}
|
|
|
|
function onComment(message) {
|
|
writeComment(eComment.Important, message);
|
|
}
|
|
|
|
var pendingRadiusCompensation = RADIUS_COMPENSATION_OFF;
|
|
|
|
function onRadiusCompensation() {
|
|
pendingRadiusCompensation = radiusCompensation;
|
|
}
|
|
|
|
// Rapid movements
|
|
function onRapid(x, y, z) {
|
|
forceSectionToStartWithRapid = false;
|
|
|
|
rapidMovements(x, y, z);
|
|
}
|
|
|
|
// Feed movements
|
|
function onLinear(x, y, z, feed) {
|
|
// If we are allowing Rapids to be recovered from Linear (cut) moves, which is
|
|
// only required when F360 Personal edition is used, then if this Linear (cut)
|
|
// move is the first operationin a Section (milling operation) then convert it
|
|
// to a Rapid. This is OK because Sections normally begin with a Rapid to move
|
|
// to the first cutting location but these Rapids were changed to Linears by
|
|
// the personal edition. If this Rapid is not recovered and feedrate scaling
|
|
// is enabled then the first move to the start of a section will be at the
|
|
// slowest cutting feedrate, generally Z's feedrate.
|
|
|
|
if (properties.mapD_RestoreFirstRapids && (forceSectionToStartWithRapid == true)) {
|
|
writeComment(eComment.Important, " First G1 --> G0");
|
|
|
|
forceSectionToStartWithRapid = false;
|
|
onRapid(x, y, z);
|
|
}
|
|
else if (isSafeToRapid(x, y, z)) {
|
|
writeComment(eComment.Important, " Safe G1 --> G0");
|
|
|
|
onRapid(x, y, z);
|
|
}
|
|
else {
|
|
linearMovements(x, y, z, feed, true);
|
|
}
|
|
}
|
|
|
|
function onRapid5D(_x, _y, _z, _a, _b, _c) {
|
|
forceSectionToStartWithRapid = false;
|
|
|
|
error(localize("Multi-axis motion is not supported."));
|
|
}
|
|
|
|
function onLinear5D(_x, _y, _z, _a, _b, _c, feed) {
|
|
forceSectionToStartWithRapid = false;
|
|
|
|
error(localize("Multi-axis motion is not supported."));
|
|
}
|
|
|
|
function onCircular(clockwise, cx, cy, cz, x, y, z, feed) {
|
|
forceSectionToStartWithRapid = false;
|
|
|
|
if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) {
|
|
error(localize("Radius compensation cannot be activated/deactivated for a circular move."));
|
|
return;
|
|
}
|
|
circular(clockwise, cx, cy, cz, x, y, z, feed)
|
|
}
|
|
|
|
// Called on waterjet/plasma/laser cuts
|
|
var powerState = false;
|
|
|
|
function onPower(power) {
|
|
if (power != powerState) {
|
|
if (power) {
|
|
writeComment(eComment.Important, " >>> LASER Power ON");
|
|
|
|
laserOn(cutterOnCurrentPower);
|
|
} else {
|
|
writeComment(eComment.Important, " >>> LASER Power OFF");
|
|
|
|
laserOff();
|
|
}
|
|
powerState = power;
|
|
}
|
|
}
|
|
|
|
// Called on Dwell Manual NC invocation
|
|
function onDwell(seconds) {
|
|
writeComment(eComment.Important, " >>> Dwell");
|
|
if (seconds > 99999.999) {
|
|
warning(localize("Dwelling time is out of range."));
|
|
}
|
|
|
|
seconds = clamp(0.001, seconds, 99999.999);
|
|
|
|
// Firmware is Grbl
|
|
if (fw == eFirmware.GRBL) {
|
|
writeBlock(gFormat.format(4), "P" + secFormat.format(seconds));
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeBlock(gFormat.format(4), "S" + secFormat.format(seconds));
|
|
}
|
|
}
|
|
|
|
// Called with every parameter in the documment/section
|
|
function onParameter(name, value) {
|
|
|
|
// Write gcode initial info
|
|
// Product version
|
|
if (name == "generated-by") {
|
|
writeComment(eComment.Important, value);
|
|
writeComment(eComment.Important, " Posts processor: " + FileSystem.getFilename(getConfigurationPath()));
|
|
}
|
|
|
|
// Date
|
|
else if (name == "generated-at") {
|
|
writeComment(eComment.Important, " Gcode generated: " + value + " GMT");
|
|
}
|
|
|
|
// Document
|
|
else if (name == "document-path") {
|
|
writeComment(eComment.Important, " Document: " + value);
|
|
}
|
|
|
|
// Setup
|
|
else if (name == "job-description") {
|
|
writeComment(eComment.Important, " Setup: " + value);
|
|
}
|
|
|
|
// Get section comment
|
|
else if (name == "operation-comment") {
|
|
sectionComment = value;
|
|
}
|
|
|
|
else {
|
|
writeComment(eComment.Debug, " param: " + name + " = " + value);
|
|
}
|
|
}
|
|
|
|
function onMovement(movement) {
|
|
var jet = tool.isJetTool && tool.isJetTool();
|
|
var id;
|
|
|
|
switch (movement) {
|
|
case MOVEMENT_RAPID:
|
|
id = "MOVEMENT_RAPID";
|
|
break;
|
|
case MOVEMENT_LEAD_IN:
|
|
id = "MOVEMENT_LEAD_IN";
|
|
break;
|
|
case MOVEMENT_CUTTING:
|
|
id = "MOVEMENT_CUTTING";
|
|
break;
|
|
case MOVEMENT_LEAD_OUT:
|
|
id = "MOVEMENT_LEAD_OUT";
|
|
break;
|
|
case MOVEMENT_LINK_TRANSITION:
|
|
id = jet ? "MOVEMENT_BRIDGING" : "MOVEMENT_LINK_TRANSITION";
|
|
break;
|
|
case MOVEMENT_LINK_DIRECT:
|
|
id = "MOVEMENT_LINK_DIRECT";
|
|
break;
|
|
case MOVEMENT_RAMP_HELIX:
|
|
id = jet ? "MOVEMENT_PIERCE_CIRCULAR" : "MOVEMENT_RAMP_HELIX";
|
|
break;
|
|
case MOVEMENT_RAMP_PROFILE:
|
|
id = jet ? "MOVEMENT_PIERCE_PROFILE" : "MOVEMENT_RAMP_PROFILE";
|
|
break;
|
|
case MOVEMENT_RAMP_ZIG_ZAG:
|
|
id = jet ? "MOVEMENT_PIERCE_LINEAR" : "MOVEMENT_RAMP_ZIG_ZAG";
|
|
break;
|
|
case MOVEMENT_RAMP:
|
|
id = "MOVEMENT_RAMP";
|
|
break;
|
|
case MOVEMENT_PLUNGE:
|
|
id = jet ? "MOVEMENT_PIERCE" : "MOVEMENT_PLUNGE";
|
|
break;
|
|
case MOVEMENT_PREDRILL:
|
|
id = "MOVEMENT_PREDRILL";
|
|
break;
|
|
case MOVEMENT_EXTENDED:
|
|
id = "MOVEMENT_EXTENDED";
|
|
break;
|
|
case MOVEMENT_REDUCED:
|
|
id = "MOVEMENT_REDUCED";
|
|
break;
|
|
case MOVEMENT_HIGH_FEED:
|
|
id = "MOVEMENT_HIGH_FEED";
|
|
break;
|
|
case MOVEMENT_FINISH_CUTTING:
|
|
id = "MOVEMENT_FINISH_CUTTING";
|
|
break;
|
|
}
|
|
|
|
if (id == undefined) {
|
|
id = String(movement);
|
|
}
|
|
|
|
writeComment(eComment.Info, " " + id);
|
|
}
|
|
|
|
var currentSpindleSpeed = 0;
|
|
|
|
function setSpindeSpeed(_spindleSpeed, _clockwise) {
|
|
if (currentSpindleSpeed != _spindleSpeed) {
|
|
if (_spindleSpeed > 0) {
|
|
spindleOn(_spindleSpeed, _clockwise);
|
|
} else {
|
|
spindleOff();
|
|
}
|
|
currentSpindleSpeed = _spindleSpeed;
|
|
}
|
|
}
|
|
|
|
function onSpindleSpeed(spindleSpeed) {
|
|
setSpindeSpeed(spindleSpeed, tool.clockwise);
|
|
}
|
|
|
|
function onCommand(command) {
|
|
writeComment(eComment.Info, " " + getCommandStringId(command));
|
|
|
|
switch (command) {
|
|
case COMMAND_START_SPINDLE:
|
|
onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE);
|
|
return;
|
|
case COMMAND_SPINDLE_CLOCKWISE:
|
|
if (!tool.isJetTool()) {
|
|
setSpindeSpeed(spindleSpeed, true);
|
|
}
|
|
return;
|
|
case COMMAND_SPINDLE_COUNTERCLOCKWISE:
|
|
if (!tool.isJetTool()) {
|
|
setSpindeSpeed(spindleSpeed, false);
|
|
}
|
|
return;
|
|
case COMMAND_STOP_SPINDLE:
|
|
if (!tool.isJetTool()) {
|
|
setSpindeSpeed(0, true);
|
|
}
|
|
return;
|
|
case COMMAND_COOLANT_ON:
|
|
if (tool.isJetTool()) {
|
|
// F360 doesn't support coolant with jet tools (water jet/laser/plasma) but we've
|
|
// added a parameter to force a coolant to be selected for jet tool operations. Note: tool.coolant
|
|
// is not used as F360 doesn't define it.
|
|
|
|
if (properties.cutter7_Coolant != eCoolant.Off) {
|
|
setCoolant(properties.cutter7_Coolant);
|
|
}
|
|
}
|
|
else {
|
|
setCoolant(tool.coolant);
|
|
}
|
|
return;
|
|
case COMMAND_COOLANT_OFF:
|
|
setCoolant(eCoolant.Off); //COOLANT_DISABLED
|
|
return;
|
|
case COMMAND_LOCK_MULTI_AXIS:
|
|
return;
|
|
case COMMAND_UNLOCK_MULTI_AXIS:
|
|
return;
|
|
case COMMAND_BREAK_CONTROL:
|
|
return;
|
|
case COMMAND_TOOL_MEASURE:
|
|
if (!tool.isJetTool()) {
|
|
probeTool();
|
|
}
|
|
return;
|
|
case COMMAND_STOP:
|
|
writeBlock(mFormat.format(0));
|
|
return;
|
|
}
|
|
}
|
|
|
|
function resetAll() {
|
|
xOutput.reset();
|
|
yOutput.reset();
|
|
zOutput.reset();
|
|
fOutput.reset();
|
|
}
|
|
|
|
function writeInformation() {
|
|
// Calcualte the min/max ranges across all sections
|
|
var toolZRanges = {};
|
|
var vectorX = new Vector(1, 0, 0);
|
|
var vectorY = new Vector(0, 1, 0);
|
|
var ranges = {
|
|
x: { min: undefined, max: undefined },
|
|
y: { min: undefined, max: undefined },
|
|
z: { min: undefined, max: undefined },
|
|
};
|
|
var handleMinMax = function (pair, range) {
|
|
var rmin = range.getMinimum();
|
|
var rmax = range.getMaximum();
|
|
if (pair.min == undefined || pair.min > rmin) {
|
|
pair.min = rmin;
|
|
}
|
|
if (pair.max == undefined || pair.max < rmin) { // was pair.min - changed by DG 1/4/2021
|
|
pair.max = rmax;
|
|
}
|
|
}
|
|
|
|
var numberOfSections = getNumberOfSections();
|
|
for (var i = 0; i < numberOfSections; ++i) {
|
|
var section = getSection(i);
|
|
var tool = section.getTool();
|
|
var zRange = section.getGlobalZRange();
|
|
var xRange = section.getGlobalRange(vectorX);
|
|
var yRange = section.getGlobalRange(vectorY);
|
|
handleMinMax(ranges.x, xRange);
|
|
handleMinMax(ranges.y, yRange);
|
|
handleMinMax(ranges.z, zRange);
|
|
if (is3D()) {
|
|
if (toolZRanges[tool.number]) {
|
|
toolZRanges[tool.number].expandToRange(zRange);
|
|
} else {
|
|
toolZRanges[tool.number] = zRange;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Display the Range Table
|
|
writeComment(eComment.Info, " ");
|
|
writeComment(eComment.Info, " Ranges Table:");
|
|
writeComment(eComment.Info, " X: Min=" + xyzFormat.format(ranges.x.min) + " Max=" + xyzFormat.format(ranges.x.max) + " Size=" + xyzFormat.format(ranges.x.max - ranges.x.min));
|
|
writeComment(eComment.Info, " Y: Min=" + xyzFormat.format(ranges.y.min) + " Max=" + xyzFormat.format(ranges.y.max) + " Size=" + xyzFormat.format(ranges.y.max - ranges.y.min));
|
|
writeComment(eComment.Info, " Z: Min=" + xyzFormat.format(ranges.z.min) + " Max=" + xyzFormat.format(ranges.z.max) + " Size=" + xyzFormat.format(ranges.z.max - ranges.z.min));
|
|
|
|
// Display the Tools Table
|
|
writeComment(eComment.Info, " ");
|
|
writeComment(eComment.Info, " Tools Table:");
|
|
var tools = getToolTable();
|
|
if (tools.getNumberOfTools() > 0) {
|
|
for (var i = 0; i < tools.getNumberOfTools(); ++i) {
|
|
var tool = tools.getTool(i);
|
|
var comment = " T" + toolFormat.format(tool.number) + " D=" + xyzFormat.format(tool.diameter) + " CR=" + xyzFormat.format(tool.cornerRadius);
|
|
if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) {
|
|
comment += " TAPER=" + taperFormat.format(tool.taperAngle) + "deg";
|
|
}
|
|
if (toolZRanges[tool.number]) {
|
|
comment += " - ZMIN=" + xyzFormat.format(toolZRanges[tool.number].getMinimum());
|
|
}
|
|
comment += " - " + getToolTypeName(tool.type) + " " + tool.comment;
|
|
writeComment(eComment.Info, comment);
|
|
}
|
|
}
|
|
|
|
// Display the Feedrate and Scaling Properties
|
|
writeComment(eComment.Info, " ");
|
|
writeComment(eComment.Info, " Feedrate and Scaling Properties:");
|
|
writeComment(eComment.Info, " Feed: Travel speed X/Y = " + properties.fr0_TravelSpeedXY);
|
|
writeComment(eComment.Info, " Feed: Travel Speed Z = " + properties.fr1_TravelSpeedZ);
|
|
writeComment(eComment.Info, " Feed: Enforce Feedrate = " + properties.fr2_EnforceFeedrate);
|
|
writeComment(eComment.Info, " Feed: Scale Feedrate = " + properties.frA_ScaleFeedrate);
|
|
writeComment(eComment.Info, " Feed: Max XY Cut Speed = " + properties.frB_MaxCutSpeedXY);
|
|
writeComment(eComment.Info, " Feed: Max Z Cut Speed = " + properties.frC_MaxCutSpeedZ);
|
|
writeComment(eComment.Info, " Feed: Max Toolpath Speed = " + properties.frD_MaxCutSpeedXYZ);
|
|
|
|
// Display the G1->G0 Mapping Properties
|
|
writeComment(eComment.Info, " ");
|
|
writeComment(eComment.Info, " G1->G0 Mapping Properties:");
|
|
writeComment(eComment.Info, " Map: First G1 -> G0 Rapid = " + properties.mapD_RestoreFirstRapids);
|
|
writeComment(eComment.Info, " Map: G1s -> G0 Rapids = " + properties.mapE_RestoreRapids);
|
|
writeComment(eComment.Info, " Map: SafeZ Mode = " + eSafeZ.prop[safeZMode].name + " : default = " + safeZHeightDefault);
|
|
writeComment(eComment.Info, " Map: Allow Rapid Z = " + properties.mapG_AllowRapidZ);
|
|
|
|
writeComment(eComment.Info, " ");
|
|
}
|
|
|
|
function writeFirstSection() {
|
|
// Write out the information block at the beginning of the file
|
|
writeInformation();
|
|
|
|
writeComment(eComment.Important, " *** START begin ***");
|
|
|
|
if (properties.gcodeStartFile == "") {
|
|
Start();
|
|
} else {
|
|
loadFile(properties.gcodeStartFile);
|
|
}
|
|
|
|
writeComment(eComment.Important, " *** START end ***");
|
|
writeComment(eComment.Important, " ");
|
|
}
|
|
|
|
// Output a comment
|
|
function writeComment(level, text) {
|
|
if (level <= properties.job3_CommentLevel) {
|
|
if (fw == eFirmware.GRBL) {
|
|
writeln("(" + String(text).replace(/[\(\)]/g, "") + ")");
|
|
}
|
|
else {
|
|
writeln(";" + String(text).replace(/[\(\)]/g, ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rapid movements with G1 and differentiated travel speeds for XY
|
|
// Changes F360 current XY.
|
|
// No longer called for general Rapid only for probing, homing, etc.
|
|
function rapidMovementsXY(_x, _y) {
|
|
let x = xOutput.format(_x);
|
|
let y = yOutput.format(_y);
|
|
|
|
if (x || y) {
|
|
if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) {
|
|
error(localize("Radius compensation mode cannot be changed at rapid traversal."));
|
|
}
|
|
else {
|
|
let f = fOutput.format(propertyMmToUnit(properties.fr0_TravelSpeedXY));
|
|
writeBlock(gMotionModal.format(0), x, y, f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rapid movements with G1 and differentiated travel speeds for Z
|
|
// Changes F360 current Z
|
|
// No longer called for general Rapid only for probing, homing, etc.
|
|
function rapidMovementsZ(_z) {
|
|
let z = zOutput.format(_z);
|
|
|
|
if (z) {
|
|
if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) {
|
|
error(localize("Radius compensation mode cannot be changed at rapid traversal."));
|
|
}
|
|
else {
|
|
let f = fOutput.format(propertyMmToUnit(properties.fr1_TravelSpeedZ));
|
|
writeBlock(gMotionModal.format(0), z, f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rapid movements with G1 uses the max travel rate (xy or z) and then relies on feedrate scaling
|
|
function rapidMovements(_x, _y, _z) {
|
|
|
|
rapidMovementsZ(_z);
|
|
rapidMovementsXY(_x, _y);
|
|
}
|
|
|
|
// Calculate the feedX, feedY and feedZ components
|
|
|
|
function limitFeedByXYZComponents(curPos, destPos, feed) {
|
|
if (!properties.frA_ScaleFeedrate)
|
|
return feed;
|
|
|
|
var xyz = Vector.diff(destPos, curPos); // Translate the cut so curPos is at 0,0,0
|
|
var dir = xyz.getNormalized(); // Normalize vector to get a direction vector
|
|
var xyzFeed = Vector.product(dir.abs, feed); // Determine the effective x,y,z speed on each axis
|
|
|
|
// Get the max speed for each axis
|
|
let xyLimit = propertyMmToUnit(properties.frB_MaxCutSpeedXY);
|
|
let zLimit = propertyMmToUnit(properties.frC_MaxCutSpeedZ);
|
|
|
|
// Normally F360 begins a Section (a milling operation) with a Rapid to move to the beginning of the cut.
|
|
// Rapids use the defined Travel speed and the Post Processor does not depend on the current location.
|
|
// This function must know the current location in order to calculate the actual vector traveled. Without
|
|
// the first Rapid the current location is the same as the desination location, which creates a 0 length
|
|
// vector. A zero length vector is unusable and so a instead the slowest of the xyLimit or zLimit is used.
|
|
//
|
|
// Note: if Map: G1 -> Rapid is enabled in the Properties then if the first operation in a Section is a
|
|
// cut (which it should always be) then it will be converted to a Rapid. This prevents ever getting a zero
|
|
// length vector.
|
|
if (xyz.length == 0) {
|
|
var lesserFeed = (xyLimit < zLimit) ? xyLimit : zLimit;
|
|
|
|
return lesserFeed;
|
|
}
|
|
|
|
// Force the speed of each axis to be within limits
|
|
if (xyzFeed.z > zLimit) {
|
|
xyzFeed.multiply(zLimit / xyzFeed.z);
|
|
}
|
|
|
|
if (xyzFeed.x > xyLimit) {
|
|
xyzFeed.multiply(xyLimit / xyzFeed.x);
|
|
}
|
|
|
|
if (xyzFeed.y > xyLimit) {
|
|
xyzFeed.multiply(xyLimit / xyzFeed.y);
|
|
}
|
|
|
|
// Calculate the new feedrate based on the speed allowed on each axis: feedrate = sqrt(x^2 + y^2 + z^2)
|
|
// xyzFeed.length is the same as Math.sqrt((xyzFeed.x * xyzFeed.x) + (xyzFeed.y * xyzFeed.y) + (xyzFeed.z * xyzFeed.z))
|
|
|
|
// Limit the new feedrate by the maximum allowable cut speed
|
|
|
|
let xyzLimit = propertyMmToUnit(properties.frD_MaxCutSpeedXYZ);
|
|
let newFeed = (xyzFeed.length > xyzLimit) ? xyzLimit : xyzFeed.length;
|
|
|
|
if (Math.abs(newFeed - feed) > 0.01) {
|
|
return newFeed;
|
|
}
|
|
else {
|
|
return feed;
|
|
}
|
|
}
|
|
|
|
// Linear movements
|
|
function linearMovements(_x, _y, _z, _feed) {
|
|
if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) {
|
|
// ensure that we end at desired position when compensation is turned off
|
|
xOutput.reset();
|
|
yOutput.reset();
|
|
}
|
|
|
|
// Force the feedrate to be scaled (if enabled). The feedrate is projected into the
|
|
// x, y, and z axis and each axis is tested to see if it exceeds its defined max. If
|
|
// it does then the speed in all 3 axis is scaled proportionately. The resulting feedrate
|
|
// is then capped at the maximum defined cutrate.
|
|
|
|
let feed = limitFeedByXYZComponents(getCurrentPosition(), new Vector(_x, _y, _z), _feed);
|
|
|
|
let x = xOutput.format(_x);
|
|
let y = yOutput.format(_y);
|
|
let z = zOutput.format(_z);
|
|
let f = fOutput.format(feed);
|
|
|
|
if (x || y || z) {
|
|
if (pendingRadiusCompensation != RADIUS_COMPENSATION_OFF) {
|
|
error(localize("Radius compensation mode is not supported."));
|
|
} else {
|
|
writeBlock(gMotionModal.format(1), x, y, z, f);
|
|
}
|
|
} else if (f) {
|
|
if (getNextRecord().isMotion()) { // try not to output feed without motion
|
|
fOutput.reset(); // force feed on next line
|
|
} else {
|
|
writeBlock(gMotionModal.format(1), f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test if file exist/can read and load it
|
|
function loadFile(_file) {
|
|
var folder = FileSystem.getFolderPath(getOutputPath()) + PATH_SEPARATOR;
|
|
if (FileSystem.isFile(folder + _file)) {
|
|
var txt = loadText(folder + _file, "utf-8");
|
|
if (txt.length > 0) {
|
|
writeComment(eComment.Info, " --- Start custom gcode " + folder + _file);
|
|
write(txt);
|
|
writeComment("eComment.Info, --- End custom gcode " + folder + _file);
|
|
}
|
|
} else {
|
|
writeComment(eComment.Important, " Can't open file " + folder + _file);
|
|
error("Can't open file " + folder + _file);
|
|
}
|
|
}
|
|
|
|
function propertyMmToUnit(_v) {
|
|
return (_v / (unit == IN ? 25.4 : 1));
|
|
}
|
|
|
|
/*
|
|
function mergeProperties(to, from) {
|
|
for (var attrname in from) {
|
|
to[attrname] = from[attrname];
|
|
}
|
|
}
|
|
|
|
function Firmware3dPrinterLike() {
|
|
FirmwareBase.apply(this, arguments);
|
|
this.spindleEnabled = false;
|
|
}
|
|
|
|
Firmware3dPrinterLike.prototype = Object.create(FirmwareBase.prototype);
|
|
Firmware3dPrinterLike.prototype.constructor = Firmware3dPrinterLike;
|
|
*/
|
|
|
|
function Start() {
|
|
// Is Grbl?
|
|
if (fw == eFirmware.GRBL) {
|
|
writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning
|
|
writeBlock(gFeedModeModal.format(94));
|
|
writeBlock(gPlaneModal.format(17));
|
|
writeBlock(gUnitModal.format(unit == IN ? 20 : 21));
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeComment(eComment.Info, " Set Absolute Positioning");
|
|
writeComment(eComment.Info, " Units = " + (unit == IN ? "inch" : "mm"));
|
|
writeComment(eComment.Info, " Disable stepper timeout");
|
|
if (properties.job1_SetOriginOnStart) {
|
|
writeComment(eComment.Info, " Set current position to 0,0,0");
|
|
}
|
|
|
|
writeBlock(gAbsIncModal.format(90)); // Set to Absolute Positioning
|
|
writeBlock(gUnitModal.format(unit == IN ? 20 : 21)); // Set the units
|
|
writeBlock(mFormat.format(84), sFormat.format(0)); // Disable steppers timeout
|
|
|
|
if (properties.job1_SetOriginOnStart) {
|
|
writeBlock(gFormat.format(92), xFormat.format(0), yFormat.format(0), zFormat.format(0)); // Set origin to initial position
|
|
}
|
|
|
|
if (properties.probe1_OnStart && tool.number != 0 && !tool.isJetTool()) {
|
|
onCommand(COMMAND_TOOL_MEASURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
function end() {
|
|
// Is Grbl?
|
|
if (fw == eFirmware.GRBL) {
|
|
writeBlock(mFormat.format(30));
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
display_text("Job end");
|
|
}
|
|
}
|
|
|
|
function spindleOn(_spindleSpeed, _clockwise) {
|
|
// Is Grbl?
|
|
if (fw == eFirmware.GRBL) {
|
|
writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed));
|
|
writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed));
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
if (properties.job2_ManualSpindlePowerControl) {
|
|
// For manual any positive input speed assumed as enabled. so it's just a flag
|
|
if (!this.spindleEnabled) {
|
|
writeComment(eComment.Important, " >>> Spindle Speed: Manual");
|
|
askUser("Turn ON " + speedFormat.format(_spindleSpeed) + "RPM", "Spindle", false);
|
|
}
|
|
} else {
|
|
writeComment(eComment.Important, " >>> Spindle Speed " + speedFormat.format(_spindleSpeed));
|
|
writeBlock(mFormat.format(_clockwise ? 3 : 4), sOutput.format(spindleSpeed));
|
|
}
|
|
this.spindleEnabled = true;
|
|
}
|
|
}
|
|
|
|
function spindleOff() {
|
|
// Is Grbl?
|
|
if (fw == eFirmware.GRBL) {
|
|
writeBlock(mFormat.format(5));
|
|
}
|
|
|
|
//Default
|
|
else {
|
|
if (properties.job2_ManualSpindlePowerControl) {
|
|
writeBlock(mFormat.format(300), sFormat.format(300), pFormat.format(3000));
|
|
askUser("Turn OFF spindle", "Spindle", false);
|
|
} else {
|
|
writeBlock(mFormat.format(5));
|
|
}
|
|
this.spindleEnabled = false;
|
|
}
|
|
}
|
|
|
|
function display_text(txt) {
|
|
// Firmware is Grbl
|
|
if (fw == eFirmware.GRBL) {
|
|
// Don't display text
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeBlock(mFormat.format(117), (properties.job8_SeparateWordsWithSpace ? "" : " ") + txt);
|
|
}
|
|
}
|
|
|
|
function circular(clockwise, cx, cy, cz, x, y, z, feed) {
|
|
if (!properties.job4_UseArcs) {
|
|
linearize(tolerance);
|
|
return;
|
|
}
|
|
|
|
var start = getCurrentPosition();
|
|
|
|
// Firmware is Grbl
|
|
if (fw == eFirmware.GRBL) {
|
|
if (isFullCircle()) {
|
|
if (isHelical()) {
|
|
linearize(tolerance);
|
|
return;
|
|
}
|
|
switch (getCircularPlane()) {
|
|
case PLANE_XY:
|
|
writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed));
|
|
break;
|
|
case PLANE_ZX:
|
|
writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed));
|
|
break;
|
|
case PLANE_YZ:
|
|
writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), yOutput.format(y), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed));
|
|
break;
|
|
default:
|
|
linearize(tolerance);
|
|
}
|
|
} else {
|
|
switch (getCircularPlane()) {
|
|
case PLANE_XY:
|
|
writeBlock(gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed));
|
|
break;
|
|
case PLANE_ZX:
|
|
writeBlock(gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed));
|
|
break;
|
|
case PLANE_YZ:
|
|
writeBlock(gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), fOutput.format(feed));
|
|
break;
|
|
default:
|
|
linearize(tolerance);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
// Marlin supports arcs only on XY plane
|
|
if (isFullCircle()) {
|
|
if (isHelical()) {
|
|
linearize(tolerance);
|
|
return;
|
|
}
|
|
switch (getCircularPlane()) {
|
|
case PLANE_XY:
|
|
writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed));
|
|
break;
|
|
default:
|
|
linearize(tolerance);
|
|
}
|
|
} else {
|
|
switch (getCircularPlane()) {
|
|
case PLANE_XY:
|
|
writeBlock(gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), fOutput.format(feed));
|
|
break;
|
|
default:
|
|
linearize(tolerance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function askUser(text, title, allowJog) {
|
|
// Firmware is RepRap?
|
|
if (fw == eFirmware.REPRAP) {
|
|
var v1 = " P\"" + text + "\" R\"" + title + "\" S3";
|
|
var v2 = allowJog ? " X1 Y1 Z1" : "";
|
|
writeBlock(mFormat.format(291), (properties.job8_SeparateWordsWithSpace ? "" : " ") + v1 + v2);
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeBlock(mFormat.format(0), (properties.job8_SeparateWordsWithSpace ? "" : " ") + text);
|
|
}
|
|
}
|
|
|
|
function toolChange() {
|
|
// Grbl tool change?
|
|
if (fw == eFirmware.GRBL) {
|
|
|
|
writeBlock(mFormat.format(6), tFormat.format(tool.number));
|
|
writeBlock(gFormat.format(54));
|
|
}
|
|
|
|
// Default tool change
|
|
else
|
|
{
|
|
flushMotions();
|
|
|
|
// Go to tool change position
|
|
onRapid(propertyMmToUnit(properties.toolChange1_X), propertyMmToUnit(properties.toolChange2_Y), propertyMmToUnit(properties.toolChange3_Z));
|
|
|
|
flushMotions();
|
|
|
|
// turn off spindle and coolant
|
|
onCommand(COMMAND_COOLANT_OFF);
|
|
onCommand(COMMAND_STOP_SPINDLE);
|
|
if (!properties.job2_ManualSpindlePowerControl) {
|
|
// Beep
|
|
writeBlock(mFormat.format(300), sFormat.format(400), pFormat.format(2000));
|
|
}
|
|
|
|
// Disable Z stepper
|
|
if (properties.toolChange4_DisableZStepper) {
|
|
askUser("Z Stepper will disabled. Wait for STOP!!", "Tool change", false);
|
|
writeBlock(mFormat.format(17), 'Z'); // Disable steppers timeout
|
|
}
|
|
// Ask tool change and wait user to touch lcd button
|
|
askUser("Tool " + tool.number + " " + tool.comment, "Tool change", true);
|
|
|
|
// Run Z probe gcode
|
|
if (properties.probe2_OnToolChange && tool.number != 0) {
|
|
onCommand(COMMAND_TOOL_MEASURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
function probeTool() {
|
|
// Is Grbl?
|
|
if (fw == eFirmware.GRBL) {
|
|
writeComment(eComment.Important, " >>> WARNING: No probing implemented for GRBL");
|
|
}
|
|
|
|
// Default
|
|
else {
|
|
writeComment(eComment.Important, " Probe to Zero Z");
|
|
writeComment(eComment.Info, " Ask User to Attach the Z Probe");
|
|
writeComment(eComment.Info, " Do Probing");
|
|
writeComment(eComment.Info, " Set Z to probe thickness: " + zFormat.format(propertyMmToUnit(properties.probe3_Thickness)))
|
|
if (properties.toolChange3_Z != "") {
|
|
writeComment(eComment.Info, " Retract the tool to " + propertyMmToUnit(properties.toolChange3_Z));
|
|
}
|
|
writeComment(eComment.Info, " Ask User to Remove the Z Probe");
|
|
|
|
askUser("Attach ZProbe", "Probe", false);
|
|
// refer http://marlinfw.org/docs/gcode/G038.html
|
|
if (properties.probe4_UseHomeZ) {
|
|
writeBlock(gFormat.format(28), 'Z');
|
|
} else {
|
|
writeBlock(gMotionModal.format(38.3), fFormat.format(propertyMmToUnit(properties.probe6_G38Speed)), zFormat.format(propertyMmToUnit(properties.probe5_G38Target)));
|
|
}
|
|
|
|
let z = zFormat.format(propertyMmToUnit(properties.probe3_Thickness));
|
|
writeBlock(gFormat.format(92), z); // Set origin to initial position
|
|
|
|
resetAll();
|
|
if (properties.toolChange3_Z != "") { // move up tool to safe height again after probing
|
|
rapidMovementsZ(propertyMmToUnit(properties.toolChange3_Z), false);
|
|
}
|
|
|
|
flushMotions();
|
|
|
|
askUser("Detach ZProbe", "Probe", false);
|
|
}
|
|
} |