Robo3D / roboOctoprint

Octoprint tailored for Robo printers.
GNU Affero General Public License v3.0
3 stars 4 forks source link

Create a Z-Offset warning on the web dashboard #18

Open Ximidar opened 6 years ago

Ximidar commented 6 years ago

Feature Development

What is this Feature?

Create a message on the web dash that will pop up when the M206 offset is not in the range between 0 and -20. 0 should pop the warning and -20 should pop the warning

Web Dash

Find the source code for the files system on the web dash and write a piece of code that will fire before the print command is issued that will check if the offset is ready for printing.

Tasks

Here is the load file function that will decide if a print will go off or not. It is located in "roboOctoprint/src/octoprint/static/js/app/viewmodels/files.js" line 401

        self.loadFile = function(file, printAfterLoad) {
            if (!file) {
                return;
            }
            var withinPrintDimensions = self.evaluatePrintDimensions(file, true);
            var print = printAfterLoad && withinPrintDimensions;

            OctoPrint.files.select(file.origin, file.path, print);
        };

Adding another check to the var print line would be the easiest solution here.

This needs to be a simple Modal screen that has a title, a body of text, and two buttons (Cancel, Continue). Alternatively you should search for other prebuilt HTML popups that foosel may have already built. (roboOctoprint/src/octoprint/static/js/app/bindings/popover.js) See if you can use this.

M503 will show you the current EEPROM, then you can parse out what you need however you wish. http://docs.octoprint.org/en/1.3.3/jsclientlib/control.html#OctoPrintClient.control.sendGcode Here is how to send a command to the printer.

terminal.js somehow gets the output from Marlin, Study that for how to get the output.

OutsourcedGuru commented 6 years ago

Interesting note... neither terminal.js nor any of the python scripts appear to be a call to the serial functionality which include a callback function.

English version of that: it's okay to send sendGcode() but you then have to parse the log it produces; there's no immediate feedback from that call.

Ximidar commented 6 years ago

RoboLCD actually works similarly. The sending command just sends, and the receiving code has to parse through the output to find the information we want.

It would be an okay process to call M501, Start the parser to capture the Z-Offset, Shut down the parser after it returns the right information, then display the warning or start the print. This will lag the interface a little bit though since it will have to wait for information to be returned before it reacts, but it would be worse to have a bed crash.

Ximidar commented 6 years ago

for finding the Z-Offset in roboLCD we go through this code:

This is a condensed version of code, we also use this to find other parts of the EEPROM, but I'll show you just the code for finding the Z-Offset

    def on_printer_add_message(self, data):
        find_data = ['M206']

        acceptable_finds = {
                            'M206': self.find_M206
        }

        for query in find_data:
            found = data.find(query)

            if found != -1:
                #execute dictionary function
                acceptable_finds[query](data)
                break        

find m206 looks like this

#home offset
    def find_M206(self, data):
        self.home_offset = self.merge_dicts(self.home_offset, self.parse_M_commands(data, "M206"))

This is the parse M command function

    def parse_M_commands(self, data, command):
        return_dict = {}
        #cut M command
        data = data.replace(command, "")
        #remove all spaces
        data = data.replace(" ", "")

        acceptable_data = ['X', 'Y', 'Z', 'E', 'P', 'I', 'D', 'R', 'T' , 'S', 'B', 'K']

        while [x for x in acceptable_data if (x in data)] != []: #this is the equivalent of if 'X' in data or 'Y' in data or 'Z' in data ect
            if data.find("X") != -1:
                var_data = self.scrape_data(data, "X")
                end_var_data = var_data.replace('X','')
                return_dict['X'] = float(end_var_data)    
                data = data.replace(var_data, '')

            elif data.find("Y") != -1:
                var_data = self.scrape_data(data, "Y")
                end_var_data = var_data.replace('Y','')
                return_dict['Y'] = float(end_var_data)    
                data = data.replace(var_data, '')

            elif data.find("Z") != -1:
                var_data = self.scrape_data(data, "Z")
                end_var_data = var_data.replace('Z','')
                return_dict['Z'] = float(end_var_data)    
                data = data.replace(var_data, '')
        return return_dict

Here are the helper functions for that

    def scrape_data(self, data, scraper):
        start_pos = data.find(scraper)

        if start_pos == -1:
            print("Cannot find data for scraper: " + str(scraper))
            return False

        end_pos = self.find_next_space(data[start_pos:len(data)], scraper)

        scraped = data[start_pos:start_pos + end_pos]

        return scraped

    def find_next_space(self, data, scraper):
        counter = 0
        acceptable_input = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', scraper]
        for i in data:
            counter += 1
            if i not in acceptable_input:
                break

        if counter == len(data):
            return len(data)
        else:
            return counter -1
    def merge_dicts(self, *dict_args):
        result = {}
        for dictionary in dict_args:
            result.update(dictionary)
        return result

All this code does is return a dictionary with M206s X,Y, and Z variables. The PConsole class will then hold the master dictionary with all EEPROM values. You might not need to get this in depth with what you return, you might just want to return the Z value and not an entire dictionary.

Hopefully this helps you in some way.

OutsourcedGuru commented 6 years ago

New knowledge: Having gone to school on the Flask/factory mechanism, this looks like it's a way of talking to the API side of OctoPrint. This is why—in JavaScript—you have access to an OctoPrint.connection object since the code is there in connection.py.

Unfortunately, there isn't a COMM class in the API so punching through into an OctoPrint.comm object isn't possible.