Add a trigger for a user gcode file after print header is read
-
It would be useful to be able to have a user gcode macro executed after the header portion of a print file is read. Possibly a fixed name file like "post_print_header.g" that gets executed if it exists.
This would let me write macros that interact with data I've automatically included in the header. One specific thing would be to stop the print if the slicer selected the wrong printer. I might also be able to log print start times, user stats, etc if I include the right data in a custom gcode header.
One challenge I don't have a solution for is how to know when the header stops and print execution begins, but maybe there is some convention with print files.
-
@mikeabuilder said in Add a trigger for a user gcode file after print header is read:
This would let me write macros that interact with data I've automatically included in the header.
How do you plan to do that?
-
I would put a line in my custom gcode header for this printer defining a variable, say printer_type = "fred". Then, when this new piece of code is called, the printer type variable should be set to "fred" if it is not, then I assume the slicer did not add the line in it's header because the wrong printer was selected in the slicer, and I cancel the print, with a message to the user. And I have some code in start.g that sets printer_type to "none" so I clear the variable before each print.
There is a longer discussion of this in this thread: https://forum.duet3d.com/topic/25763/implementing-a-slicer-checker
-
@mikeabuilder said in Add a trigger for a user gcode file after print header is read:
I would put a line in my custom gcode header for this printer defining a variable, say printer_type = "fred". Then, when this new piece of code is called, the printer type variable should be set to "fred" if it is not, then I assume the slicer did not add the line in it's header because the wrong printer was selected in the slicer, and I cancel the print, with a message to the user. And I have some code in start.g that sets printer_type to "none" so I clear the variable before each print.
There is a longer discussion of this in this thread: https://forum.duet3d.com/topic/25763/implementing-a-slicer-checker
There is a big difference between extracting information from the head information generated by the slicer and generating you own data using the custom gcode feature of the slicer.
I was under the impression that you wanted a solution that didn't rely on using slicer custom code?
Frederick
-
@mikeabuilder said in Add a trigger for a user gcode file after print header is read:
I would put a line in my custom gcode header for this printer defining a variable, say printer_type = "fred".
I see, you may want to include this detail in the request here, such that the header parsing will include also custom headers you will add via the slicer's setting, rather than just the standard headers.
-
@fcwilt said in Add a trigger for a user gcode file after print header is read:
I was under the impression that you wanted a solution that didn't rely on using slicer custom code?
I think that he wants the printer to verify that that file contains some evidence that it was generated with that printer in mind and generating the gocde with a printer specific header value may be that evidence.
-
Thanks for the discussion. @zapata and @fcwilt - for my slicer-checker implementation, I expect that I will have the slicer include the setting of a global variable in the custom header code. But I do not plan to try to change the slicer software itself.
I've not suggested using information that would already be in a print file's gcode header because I don't know what that information might be. I'm completely ignorant in that area. I looked at one file that my PrusaSlicer generated and nothing looked like "header information" to me.
If there is a information that exists in the header, it might be nice for to be able to access it for other possible uses of this feature.
-
@mikeabuilder different slicers add lots of varying information to headers &/footer comments. some of that is already parsed to work out e.g. what slicer was used, for display in DWC:
e.g.:
I see feature request as two fold:
- Add another field to be parsed.
- Add a method you can optionally call to check and reject the files that do not have the correct vlaue in the field mentioned in point 1.
That could be on upload, or it could be on print. I assume on upload would be easier as thats when the file is parsed already.
That said @dc42 may have a much better way to achieve the same thing.
This is unlikely to get addressed immediately do in the sort/medium term I would use the deamon.g method I already mentioned and have your valid files set a global variable.
-
@mikeabuilder said in Add a trigger for a user gcode file after print header is read:
I expect that I will have the slicer include the setting of a global variable in the custom header code.
Given the way variables work and that you have access to the object model it would be simpler, I think, to have the custom slicer code check the printer name which you can access in the object model.
Something like:
if {network.name} != "expected_name" abort "Invalid Printer Name"
I looked at one file that my PrusaSlicer generated and nothing looked like "header information" to me.
That's part of the problem - every slicer does things differently in this area. By default Prusa includes very little info and I don't know if it can be added to. By comparison look at the information Simplifi3D adds as "header information":
; G-Code generated by Simplify3D(R) Version 4.1.2 ; Nov 2, 2021 at 7:09:49 PM ; Settings Summary ; processName,FT5 90 ; applyToModels,Cable Bracket for Frame v2 ; profileName,FT-5 TBO=555 SPD=90 (modified) ; profileVersion,2021-10-14 21:59:01 ; baseProfile,Folger Tech FT-5 ; printMaterial,PLA ; printQuality,Medium ; printExtruders, ; extruderName,Primary Extruder ; extruderToolheadNumber,0 ; extruderDiameter,0.4 ; extruderAutoWidth,1 ; extruderWidth,0.48 ; extrusionMultiplier,0.9 ; extruderUseRetract,1 ; extruderRetractionDistance,0.8 ; extruderExtraRestartDistance,0 ; extruderRetractionZLift,0 ; extruderRetractionSpeed,1800 ; extruderUseCoasting,1 ; extruderCoastingDistance,0.2 ; extruderUseWipe,0 ; extruderWipeDistance,5 ; primaryExtruder,0 ; layerHeight,0.2 ; topSolidLayers,5 ; bottomSolidLayers,5 ; perimeterOutlines,5 ; printPerimetersInsideOut,1 ; startPointOption,2 ; startPointOriginX,0 ; startPointOriginY,150 ; sequentialIslands,0 ; spiralVaseMode,0 ; firstLayerHeightPercentage,150 ; firstLayerWidthPercentage,100 ; firstLayerUnderspeed,0.1 ; useRaft,0 ; raftExtruder,0 ; raftTopLayers,3 ; raftBaseLayers,2 ; raftOffset,3 ; raftSeparationDistance,0.14 ; raftTopInfill,100 ; aboveRaftSpeedMultiplier,0.3 ; useSkirt,1 ; skirtExtruder,0 ; skirtLayers,2 ; skirtOutlines,2 ; skirtOffset,3 ; usePrimePillar,0 ; primePillarExtruder,999 ; primePillarWidth,12 ; primePillarLocation,7 ; primePillarSpeedMultiplier,1 ; useOozeShield,0 ; oozeShieldExtruder,999 ; oozeShieldOffset,2 ; oozeShieldOutlines,1 ; oozeShieldSidewallShape,1 ; oozeShieldSidewallAngle,30 ; oozeShieldSpeedMultiplier,1 ; infillExtruder,0 ; internalInfillPattern,Grid ; externalInfillPattern,Rectilinear ; infillPercentage,20 ; outlineOverlapPercentage,15 ; infillExtrusionWidthPercentage,100 ; minInfillLength,5 ; infillLayerInterval,1 ; internalInfillAngles,45,-45 ; overlapInternalInfillAngles,1 ; externalInfillAngles,-45,45 ; generateSupport,0 ; supportExtruder,0 ; supportInfillPercentage,50 ; supportExtraInflation,0 ; supportBaseLayers,0 ; denseSupportExtruder,0 ; denseSupportLayers,0 ; denseSupportInfillPercentage,70 ; supportLayerInterval,1 ; supportHorizontalPartOffset,0.3 ; supportUpperSeparationLayers,1 ; supportLowerSeparationLayers,1 ; supportType,0 ; supportGridSpacing,1 ; maxOverhangAngle,45 ; supportAngles,0 ; temperatureName ; temperatureNumber ; temperatureSetpointCount ; temperatureSetpointLayers ; temperatureSetpointTemperatures ; temperatureStabilizeAtStartup ; temperatureHeatedBed ; fanLayers,1,2,3,4,5,6 ; fanSpeeds,0,10,20,30,40,50 ; blipFanToFullPower,0 ; adjustSpeedForCooling,1 ; minSpeedLayerTime,15 ; minCoolingSpeedSlowdown,20 ; increaseFanForCooling,0 ; minFanLayerTime,45 ; maxCoolingFanSpeed,100 ; increaseFanForBridging,0 ; bridgingFanSpeed,100 ; use5D,1 ; relativeEdistances,1 ; allowEaxisZeroing,1 ; independentExtruderAxes,0 ; includeM10123,0 ; stickySupport,1 ; applyToolheadOffsets,0 ; gcodeXoffset,0 ; gcodeYoffset,0 ; gcodeZoffset,0 ; overrideMachineDefinition,1 ; machineTypeOverride,0 ; strokeXoverride,300 ; strokeYoverride,300 ; strokeZoverride,350 ; originOffsetXoverride,150 ; originOffsetYoverride,150 ; originOffsetZoverride,0 ; homeXdirOverride,1 ; homeYdirOverride,1 ; homeZdirOverride,-1 ; flipXoverride,1 ; flipYoverride,-1 ; flipZoverride,1 ; toolheadOffsets,0,0|0,0|0,0|0,0|0,0|0,0 ; overrideFirmwareConfiguration,0 ; firmwareTypeOverride,RepRap (Marlin/Repetier/Sprinter) ; GPXconfigOverride,r2 ; baudRateOverride,250000 ; overridePrinterModels,1 ; printerModelsOverride ; startingGcode,M98 P"print_begin.g",, ; layerChangeGcode,M98 P"print_layer_change.g", ; retractionGcode,M98 P"print_retraction.g", ; toolChangeGcode, ; endingGcode,M98 P"print_end.g",, ; exportFileFormat,gcode ; celebration,0 ; celebrationSong,Random Song ; postProcessing, ; defaultSpeed,5400 ; outlineUnderspeed,0.5 ; solidInfillUnderspeed,0.8 ; supportUnderspeed,0.8 ; rapidXYspeed,5400 ; rapidZspeed,600 ; minBridgingArea,50 ; bridgingExtraInflation,0 ; bridgingExtrusionMultiplier,1 ; bridgingSpeedMultiplier,1 ; useFixedBridgingAngle,0 ; fixedBridgingAngle,0 ; applyBridgingToPerimeters,0 ; filamentDiameters,1.75|1.75|1.75|1.75|1.75|1.75 ; filamentPricesPerKg,0|46|46|46|46|46 ; filamentDensities,1.25|1.25|1.25|1.25|1.25|1.25 ; useMinPrintHeight,0 ; minPrintHeight,0 ; useMaxPrintHeight,0 ; maxPrintHeight,0 ; useDiaphragm,0 ; diaphragmLayerInterval,20 ; robustSlicing,1 ; mergeAllIntoSolid,0 ; onlyRetractWhenCrossingOutline,1 ; retractBetweenLayers,1 ; useRetractionMinTravel,0 ; retractionMinTravel,3 ; retractWhileWiping,0 ; onlyWipeOutlines,1 ; avoidCrossingOutline,0 ; maxMovementDetourFactor,3 ; toolChangeRetractionDistance,12 ; toolChangeExtraRestartDistance,-0.5 ; toolChangeRetractionSpeed,600 ; externalThinWallType,0 ; internalThinWallType,1 ; thinWallAllowedOverlapPercentage,10 ; singleExtrusionMinLength,1 ; singleExtrusionMinPrintingWidthPercentage,50 ; singleExtrusionMaxPrintingWidthPercentage,200 ; singleExtrusionEndpointExtension,0.2 ; horizontalSizeCompensation,0
-
@t3p3tony - Thanks for the clarifications. I just read up on daemon.g and now that I understand it, I do think that will work for my purposes.
-
@fcwilt i think you miss the point that this must work to reject files that do not have something set in the print file.
so adding:
;MYSPECIALMAGICNAME=ValidNameor whatever to the start gcode in the slicer would be parsed as long as was in the limits of whatever is parsed from a file, and the parsing element was extended to look for it.
-
@t3p3tony said in Add a trigger for a user gcode file after print header is read:
@fcwilt i think you miss the point that this must work to reject files that do not have something set in the print file.
so adding:
;MYSPECIALMAGICNAME=ValidNameor whatever to the start gcode in the slicer would be parsed as long as was in the limits of whatever is parsed from a file, and the parsing element was extended to look for it.
The OP mentioned the situation where the slicer was setup for the wrong printer. So what difference does it make where the check is done, be it in the slicer custom code or in some macro invoked at the start of a print?
Frederick
-
@fcwilt because if a 3rd party slicer is used to generate the gcode - this check needs to work (and reject the gcode file). So a solution that works by assuming the slicer has the right check in its header does not cover that pert of the requirement.
-
@t3p3tony said in Add a trigger for a user gcode file after print header is read:
@fcwilt because if a 3rd party slicer is used to generate the gcode - this check needs to work (and reject the gcode file). So a solution that works by assuming the slicer has the right check in its header does not cover that pert of the requirement.
Understood but the OP mentioned setting a global variable in the slicer custom code. That approach also relies on an assumption - that the slicer set the variable - does it not?
Frederick
-
@fcwilt if the variable is not set (or comment flag in the header is not there) then the file will be rejected and not printed in the method we are proposing. If the check is in the start gcode then 3rd party files without a check will not be rejected. That's why two elements are needed. An identified of some sort in the file, and a process to check that identified, either when any file is uploaded, or printed.
-
@t3p3tony said in Add a trigger for a user gcode file after print header is read:
@fcwilt if the variable is not set (or comment flag in the header is not there) then the file will be rejected and not printed in the method we are proposing. If the check is in the start gcode then 3rd party files without a check will not be rejected. That's why two elements are needed. An identified of some sort in the file, and a process to check that identified, either when any file is uploaded, or printed.
OK that much is clear.
But without a way to protect the Duet code from modification there doesn't seem to be much to prevent bypassing the check.
Well, as long as the OP is happy that is all that matters.
Frederick
-
@fcwilt yes this is not a high security measure but it would prevent naive users from printing their own sliced gcode on the wrong machines.
-
@t3p3tony has the gist of my objective. I think the initial implementation using daemon.g looks like this:
in start.g:
Set a variable called "stop_print = true"In daemon.g:
Check the value of "stop_print". If true, abort the print, if not true, continue.In the slicer, I put a line in the custom gcode for this printer type (PrusaSlicer has this capability, I assume others do too):
Set "stop_print = false"With this setup, any slicer that does not incorporate the "stop_print =false" line will result in a print aborted within one second.
It will be possible for anyone to maliciously edit their gcode file, but I'm not worried about that user. I'm worried about the user that sliced for some other printer by accident.
-
@mikeabuilder that sounds like it should work. I would set a while loop up inside deamon.g to check stop print every (say) 2 seconds with a delay of G4 S2. You don't want deamon.g looping constantly.
-
@mikeabuilder said in Add a trigger for a user gcode file after print header is read:
@t3p3tony has the gist of my objective. I think the initial implementation using daemon.g looks like this:
in start.g:
Set a variable called "stop_print = true"In daemon.g:
Check the value of "stop_print". If true, abort the print, if not true, continue.In the slicer, I put a line in the custom gcode for this printer type (PrusaSlicer has this capability, I assume others do too):
Set "stop_print = false"With this setup, any slicer that does not incorporate the "stop_print =false" line will result in a print aborted within one second.
It will be possible for anyone to maliciously edit their gcode file, but I'm not worried about that user. I'm worried about the user that sliced for some other printer by accident.
Basically that will do what you seek.
A couple of things:
- I think the variable will have to be global
- You will need to create the variable in config.g OR have code in start.g that creates or sets the variable as needed
- The daemon.g file runs every 10 seconds - unless there has been a recent change I am not aware of
Just FYI it is quite possible to run the same sliced code on different printers. It all depends on how much in the way of printer specific elements are included in the generated code. I use at different times Simplifi3D, Cura, Prusa or ideaMaker and generally I can run any of the sliced code on any of my printers.
Best of luck!
Frederick