Jump to content

Need help with a Post Processing Script


EasyGoing1

Recommended Posts

Posted · Need help with a Post Processing Script

I'm trying to create a post processing script that finds the last layer comment and replaces it with some text.

 

I copied the Search And Replace python script and modified it, but my problem is, that I don't code in Python, so I'm really not knowledgeable enough to understand why this isn't working (I work primarily in Java).

 

Here is the script that I ended up creating, and it does show up in the Post Processing script list so I have it copied to the correct folder. The issue is that the search and replace isn't actually happening so Im hoping someone can look at this and tell me why it isn't working:

 

import re

from ..Script import Script


class PlayAtEnd(Script):
    """Adds custom command to the last layer.
    """

    def getSettingDataString(self):
        return """{
            "name": "Play At End",
            "key": "PlayAtEnd",
            "metadata": {},
            "version": 2,
            "settings":
            {
                "searchCode":
                {
                    "label": "Last Layer comment Will Be Found",
                    "description": "Replace last layer comment.",
                    "type": "str",
                    "default_value": ""
                },
                "replaceCode":
                {
                    "label": "And replaced with",
                    "description": "Replace Text",
                    "type": "str",
                    "default_value": "@FinalCountdown"
                }
            }
        }"""

    def execute(self, data):
        foundHits = re.findall(";LAYER:\\d+", data)
        lastIndex = len(foundHits) - 1
        search_string = ";LAYER:" + str(lastIndex)
        final_countdown = '@FinalCountdown\n'
        replace_string = ''.join([final_countdown, search_string])

        for layer_number, layer in enumerate(data):
            data[layer_number] = re.sub(search_string, replace_string, layer)  # Replace all.
        return data

 

  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    Hey @EasyGoing1

     

    I'm really curious, what problem are you trying to solve by changing the gcode?
    There might be a different approach that involves less coding 😉

     

    If it comes to changing Gcode I usually use the Post Processing Script
    Did you try that one yet?

    image.thumb.png.365dbd791ffd682a4d0eb74021a46a56.png

  • Link to post
    Share on other sites

    Posted (edited) · Need help with a Post Processing Script

    Hi Mari,

     

    Thank you for your reply. What I am wanting to do, is have my gcode execute an Octoprint macro just before starting the last layer of any print that I slice. And I want the insertion of that macro command to be automatic so that I dont have to do a manual search and replace every time I slice a print. 

     

    The idea seemed fairly simple to me ... using regex, do a findAll on a simple string pattern

     

    ;LAYER:\d+

     

    ... the last one found would be the start of the final layer, then simply replace that string with the macro call and it's all good.

     

    But it doesn't seem to be working out. I did learn through trying to make this work, that Prusa slicer can call any program that you desire for post processing and it will pass it the absolute file path of the gcode file that it generates as an argument to the program, where you can then further process the gcode, save it then the slicer drops it in to the folder that you chose when you export the gcode.

     

    Using a Java program that I threw together, I was able to successfully do what I want in post processing with Prusa, but I'm otherwise not familiar with Prusa slicer so im not sure how that is going to work out.

     

    Also, if this is the place to make suggestions for Cura, I think it would be great to have a post processing option that allows us to insert gcode at specific layers ... in similar fashion to how we can pause a print at any layer - which Ive used many times to print objects into parts etc. Being able to insert gcode at a given layer might be a function that others would find useful as well ... though that's just a presumption on my part of course

    Edited by EasyGoing1
  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    By the way ... where I seem to be struggling with the Python code, is where it has to engage the data object. The way that the search and replace python script iterates that object has me really confused. Which i'm sure has more to do with my lack of Python knowledge, but then when you add to that, the fact that Python has no requirement to declare datatypes, I look at the code and I don't even know what to Google to figure out what it is that the search and replace script is doing with that data object. 

     

    Specifically, this is from the stock search and replace script that comes installed by default with Cura:

            for layer_number, layer in enumerate(data):
                data[layer_number] = re.sub(search_regex, replace_string, layer) #Replace all.
    
            return data

     

    I've never seen a for next loop initialize with two different objects before in reference to a third ... I get layer_number, which might be the line number of the gcode in the data object ... but then that word layer ... not sure what that is at all and I tied working with it assuming that layer was just each line of the gcode as the iteration happens, but that doesn't seem to be true because i could not - no matter what I tried (and I must have made 20 or 30 different attempts at doing this with various changes in the script) find the last line matching the regex then replace it with my macro command.

     

    I looked at another script that comes with Cura, and its called DisplayProgressOnLCD ... and it does a similar iteration through the data object but it gets even more bizarre

    for layer in data:
        layer_index = data.index(layer)
        lines = layer.split("\n")
    
    	for line in lines:

     

    Now when I look at that code, that makes me think that layer is just the complete gcode which can be further iterated by breaking out the lines within that layer through the split call... but then this begs the question ... are there multiple different sets of gcode in the data object that it then becomes necessary to keep track of the index number for each "layer" ... I don't know ... it's very confusing and without having any kind of reference to explain what data even is ... all of my attempts to make this work, were just stabs in the dark.

  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    There used to be such a plugin.  It was called "tweak at layer" and you could add any gcode you wanted.  I'm not sure if it still exists.

     

    I wrote a plugin back for cura 14.X which is very old and different from modern Cura.  The thing was you got to look at only one line at a time and in order.  I assume it still works that way with "post processing" plugins?  If so then there's no way to know that you are on the last layer without first searching for the number of layers (found in a comment near the top of the gcode).

     

  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script
    13 minutes ago, EasyGoing1 said:
    lines = layer.split("\n")

    Yeah.  I don't know either but seeing that code I'm 90% sure "layer" holds all the gcodes for a given layer.  "split" is the same in javascript such that lines contains all the lines of gcode separated out.  Layer is almost certainly a string type.  Just a simple string.

     

    I'm sure that python (like javascript) has easy ways to insert something into a huge string like "layer".  I suspect though that if you edit a single "lines" object it won't modify the "layer" string.  Did later code put the lines back into a layer?

     

    Also in python I'm sure there is a way to just look at the final layer in the data object and not have to iterate through them all.  In fact you could probably just add a string at the start of the final layer with no regex needed.  No substitution needed.  No splitting up of the individual gcodes needed.

  • Link to post
    Share on other sites

    Posted (edited) · Need help with a Post Processing Script
    On 9/7/2022 at 7:09 AM, gr5 said:

    Yeah.  I don't know either but seeing that code I'm 90% sure "layer" holds all the gcodes for a given layer. 

    OK, then this makes sense ... and is also useful. So perhaps then the data object when first iterated, brings out the gcode for each layer which can then be iterated by separating out each line at \n using split.  Split is used in standard Java all the time as well ... it merely takes a string, then splits it into an array using whatever you pass into it as the split parameter (new line in this case).

     

    Im gonna experiment with it going on the assumption that you just pointed out ... I was most likely over-thinking things.

     

    In theory, I could iterate data once, then count the iterations and in that count, know how many layers there are in the gcode then use that number to insert my macro command.

    Edited by EasyGoing1
  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    @gr5

    I got it! Thanks to you, I went under the assumption that the first iteration of data contains an array of gcode layers ... and I came up with this really simple code (commented for explanation):

     

        def execute(self, data):
            # define our variable that will store the number of the last layer
            lastLayer = 0
    
            for layer_number, layer in enumerate(data):
                # as this keeps looping, layer_number increases,
                # so we just set lastLayer to that number since
                # the last time the loop happens, we will then be
                # assigning lastLayer the highest number
                # (minus one because array indexes start at 0)
                lastLayer = layer_number - 1
    
            # Now all we have to do is reference the lastLayer
            # (minus one again because it didn't work without that)
            # and then split the layer into an array called lines then
            # insert the custom macro command at the beginning of that
            # array.
            lines = data[lastLayer - 1].split("\n")
            lines.insert(1, '@FinalCountdown\n')
    
            # Replace the layer with the new array that has
            # the inserted command.
            data[lastLayer - 1] = "\n".join(lines)
    
            # Hand the new data object back to Cura for finalizing... 
            # All done!
            return data
        

     

    • Like 2
    Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    Looks good.  Interesting that you have to subtract 1 TWICE from layer_number.  I'm guessing that the last layer or two are not actually layer information but are instead like the finishing up stuff and final gcodes and comments at the bottom.

  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script
    1 hour ago, gr5 said:

    Looks good.  Interesting that you have to subtract 1 TWICE from layer_number.  I'm guessing that the last layer or two are not actually layer information but are instead like the finishing up stuff and final gcodes and comments at the bottom.

    Yes you are right,if I'm remember correctly the first layer it's  the first general informations , the second one it's the start Gcode. And the third it's the first layer. But in python as the list index start a 0  :

    0 comment

    1 start Gcode

    2 First layer -> 2-1=1  Layer number 1 

    note in the Gcode first layer it's layer 0  

  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script
    2 hours ago, gr5 said:

    Looks good.  Interesting that you have to subtract 1 TWICE from layer_number.  I'm guessing that the last layer or two are not actually layer information but are instead like the finishing up stuff and final gcodes and comments at the bottom.

    Thats the same line of thinking I was in too ... like theres a last layer, then there is the finishing gcode which I think must get treated as a layer as well .

  • Link to post
    Share on other sites

    Posted (edited) · Need help with a Post Processing Script

    Here is the final script which allows any text to be typed in then it will get inserted before the last layer.

     

    from ..Script import Script
    
    import re
    
    
    class RunAtEnd(Script):
    
        def getSettingDataString(self):
            return """
            {
                "name": "Run At End",
                "key": "RunAtEnd",
                "metadata": {},
                "version": 2,
                "settings":
                {
                    "insert":
                    {
                        "label": "Run before last layer",
                        "description": "The code will be inserted before the last layer.",
                        "type": "str",
                        "default_value": "Code"
                    }
                }
            }
            """
    
        def execute(self, data):
            insert_string = self.getSettingValueByKey("insert")
            lastLayer = 0
    
            for layer_number, layer in enumerate(data):
                lastLayer = layer_number - 1
    
            lines = data[lastLayer - 1].split("\n")
            lines.insert(1, insert_string)
    
            data[lastLayer - 1] = "\n".join(lines)
    
            return data

     

    Edited by EasyGoing1
    Code change
  • Link to post
    Share on other sites

    Posted · Need help with a Post Processing Script

    I'm happy you figured it out! Thanks for sharing the updates with us. 

    ok-all.gif.6ad700f7e66ca48fb1ddc4d90768836e.gif

  • Link to post
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now
    • Our picks

      • UltiMaker Cura 5.8 Stable released 🎉
        In the Cura 5.8 stable release, everyone can now tune their Z seams to look better than ever. Method series users get access to new material profiles, and the base Method model now has a printer profile, meaning the whole Method series is now supported in Cura!
        • 5 replies
      • Introducing the UltiMaker Factor 4
        We are happy to announce the next evolution in the UltiMaker 3D printer lineup: the UltiMaker Factor 4 industrial-grade 3D printer, designed to take manufacturing to new levels of efficiency and reliability. Factor 4 is an end-to-end 3D printing solution for light industrial applications
          • Thanks
          • Like
        • 3 replies
    ×
    ×
    • Create New...