예제 #1
0
 def execute(self, g):
     self.printer.running_M116 = True
     all_ok = [False, False, False]
     while True:
         all_ok[0] |= self.printer.heaters[
             'E'].is_target_temperature_reached()
         all_ok[1] |= self.printer.heaters[
             'H'].is_target_temperature_reached()
         all_ok[2] |= self.printer.heaters[
             'HBP'].is_target_temperature_reached()
         m105 = Gcode({"message": "M105", "prot": g.prot})
         self.printer.processor.execute(m105)
         if (not False in all_ok) or (self.printer.running_M116 == False):
             logging.info("Heating done.")
             self.printer.send_message(g.prot, "Heating done.")
             self.printer.reply(m105)
             self.printer.running_M116 = False
             return
         else:
             answer = m105.get_answer()
             answer += " E: " + ("0" if self.printer.current_tool == "E"
                                 else "1")
             m105.set_answer(answer[2:])  # strip away the "ok"
             self.printer.reply(m105)
             time.sleep(1)
예제 #2
0
파일: G29.py 프로젝트: fca1/redeem-1
    def execute(self, g):
        gcodes = self.printer.config.get("Macros", "G29").split("\n")
        self.printer.path_planner.wait_until_done()
        for gcode in gcodes:
            # If 'S' (imulate) remove M561 and M500 codes
            if g.has_letter("S"):
                if "RFS" in gcode:
                    logging.debug("G29: Removing due to RFS: " + str(gcode))
                else:
                    G = Gcode({"message": gcode, "prot": g.prot})
                    self.printer.processor.execute(G)
                    self.printer.path_planner.wait_until_done()
            else:  # Execute all
                G = Gcode({"message": gcode, "prot": g.prot})
                self.printer.processor.execute(G)
                self.printer.path_planner.wait_until_done()

        probe_data = copy.deepcopy(self.printer.probe_points)
        bed_data = {
            "probe_data": {
                "x": [],
                "y": [],
                "z": []
            },
            "probe_type": "test" if g.has_letter("S") else "probe",
            "replicape_key": self.printer.replicape_key
        }
        for k, v in enumerate(probe_data):
            bed_data["probe_data"]["x"].append(probe_data[k]["X"])
            bed_data["probe_data"]["y"].append(probe_data[k]["Y"])
            bed_data["probe_data"]["z"].append(self.printer.probe_heights[k])

        Alarm.action_command("bed_probe_data", json.dumps(bed_data))
예제 #3
0
 def execute(self, g):
     m104 = Gcode({
         "message": "M104 " + " ".join(g.get_tokens()),
         "prot": g.prot
     })
     self.printer.processor.execute(m104)
     m116 = Gcode({"message": "M116", "prot": g.prot})
     self.printer.processor.execute(m116)
예제 #4
0
 def execute(self, g):
     logging.debug("M109 tokens is '" + " ".join(g.get_tokens()) + "'")
     m104 = Gcode({
         "message": "M104 " + " ".join(g.get_tokens()),
         "prot": g.prot
     })
     self.printer.processor.execute(m104)
     m116 = Gcode({"message": "M116", "prot": g.prot})
     self.printer.processor.execute(m116)
예제 #5
0
 def send_temperatures(self):
     while self.running:
         m105 = Gcode({"message": "M105", "prot": self.g.prot})
         self.printer.processor.execute(m105)
         answer = m105.get_answer()
         m105.set_answer(answer[2:])  # strip away the "ok"
         self.printer.reply(m105)
         self.plot_temps.append("({}, {:10.4f})".format(time.time(), self.heater.get_temperature_raw() ))
         time.sleep(1)
     logging.debug(self.plot_temps)
예제 #6
0
    def _make_async_queue_wait_for_buffered_queue(self):
        logging.info("queueing async-to-buffered sync event")

        wait_event = self.printer.path_planner.native_planner.queueWaitEvent()

        buffered = Sync.BufferedSyncEvent(self.printer, wait_event)
        buffered_gcode = Gcode({
            "message": "SyncAsyncToBuffered_Buffered",
            "prot": "internal"
        })
        buffered_gcode.command = buffered

        self.printer.commands.put(buffered_gcode)
        logging.info("done queueing async-to-buffered sync event")
예제 #7
0
 def loop(self):
     print "Replicape starting main"
     try:
         while True:                
             gcode = Gcode(self.commands.get(), self)
             self._execute(gcode)
             if gcode.prot == "USB":
                 self.usb.send_message(gcode.getAnswer())
             elif gcode.prot == "PIPE":
                 self.pipe.send_message(gcode.getAnswer())
             else:
                 self.ethernet.send_message(gcode.getAnswer())
             self.commands.task_done()
     except Exception as e:
         logging.exception("Ooops: ")
예제 #8
0
 def loop(self):
     print "Replicape starting main"
     try:
         while True:
             gcode = Gcode(self.commands.get(), self)
             self._execute(gcode)
             if gcode.prot == "USB":
                 self.usb.send_message(gcode.getAnswer())
             elif gcode.prot == "PIPE":
                 self.pipe.send_message(gcode.getAnswer())
             else:
                 self.ethernet.send_message(gcode.getAnswer())
             self.commands.task_done()
     except Exception as e:
         logging.exception("Ooops: ")
예제 #9
0
 def get_message(self):
     """Loop that gets messages and pushes them on the queue"""
     while self.running:
         logging.info("Ethernet listening")
         self.client, self.address = self.s.accept()
         logging.info("Ethernet connection accepted")
         while True:
             line = ''
             while not "\n" in line:
                 try:
                     chunk = self.client.recv(1)
                 except socket.error, (value, message):
                     logging.error("Ethernet " + message)
                     chunk = ''
                 if chunk == '':
                     logging.warning("Ethernet: Connection reset by Per.")
                     self.client.close()
                     break
                 line = line + chunk
             if not "\n" in line:  # Make sure the whole line was read.
                 break
             message = line.strip("\n")
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": "Eth"})
                 if self.printer.processor.is_buffered(g):
                     self.printer.commands.put(g)
                 else:
                     self.printer.unbuffered_commands.put(g)
예제 #10
0
    def execute(self, g):

        gcodes = self.printer.config.get("Macros", "G29").split("\n")
        self.printer.path_planner.wait_until_done()
        for gcode in gcodes:
            G = Gcode({"message": gcode, "prot": g.prot})
            self.printer.processor.execute(G)
            self.printer.path_planner.wait_until_done()

        logging.debug(self.printer.probe_heights)

        # Remove the offset from the probed points
        if self.printer.probe_points[0][
                "X"] == 0 and self.printer.probe_points[0]["Y"] == 0:
            # If the origin is located in the first probe point, remove that.
            self.printer.probe_heights -= self.printer.probe_heights[0]
        else:
            # Else, remove the lowest.
            self.printer.probe_heights -= min(self.printer.probe_heights)

        # Log the found heights
        for k, v in enumerate(self.printer.probe_points):
            self.printer.probe_points[k]["Z"] = self.printer.probe_heights[k]
        logging.info("Found heights: ")
        logging.info(self.printer.probe_points)

        # Add 'S'=simulate To not update the bed matrix.
        if not g.has_letter("S"):
            # Update the bed compensation matrix
            Path.update_autolevel_matrix(self.printer.probe_points,
                                         self.printer.probe_heights)
            logging.debug("New Bed level matrix: ")
            logging.debug(Path.matrix_bed_comp)
예제 #11
0
 def loop(self):
     try:
         while True:                
             gcode = Gcode(self.commands.get(), self)
             self._execute(gcode)
             self.usb.send_message(gcode.getAnswer())
             self.commands.task_done()
     except KeyboardInterrupt:
         print "Caught signal, exiting" 
         return
     finally:
         self.ext1.disable()
         self.hbp.disable()
         self.usb.close() 
         self.path_planner.exit()   
     logging.debug("Done")
예제 #12
0
파일: G31.py 프로젝트: fca1/redeem-1
 def execute(self, g):
     gcodes = self.printer.config.get("Macros", "G31").split("\n")
     self.printer.path_planner.wait_until_done()
     for gcode in gcodes:        
         G = Gcode({"message": gcode, "prot": g.prot})
         self.printer.processor.execute(G)
         self.printer.path_planner.wait_until_done()
예제 #13
0
파일: USB.py 프로젝트: goeland86/redeem
 def get_message(self):
     message = self.tty.readline().strip("\n")
     if len(message) > 0:
         g = Gcode({"message": message, "prot": "USB"})
         self.printer.processor.enqueue(g)
         # Do not enable sending messages until a
         # message has been received
         self.send_response = True
예제 #14
0
 def execute(self, g):
     temperature = float(g.get_value_by_letter("S"))
     self.printer.heaters['HBP'].set_target_temperature(temperature)
     self.printer.processor.execute(
         Gcode({
             "message": "M116",
             "prot": g.prot
         }))
예제 #15
0
 def get_message(self, flags):
     try:
         message = self.rd.readline().rstrip()
         if len(message) > 0:
             g = Gcode({"message": message, "prot": self.prot})
             self.printer.processor.enqueue(g)
     except IOError:
         logging.warning("Could not read from {} pipe".format(self.prot))
예제 #16
0
파일: M116.py 프로젝트: zittix/redeem
 def execute(self,g):
     all_ok = [False, False, False]
     while True:
         all_ok[0] |= self.printer.heaters['E'].is_target_temperature_reached()
         all_ok[1] |= self.printer.heaters['H'].is_target_temperature_reached()
         all_ok[2] |= self.printer.heaters['HBP'].is_target_temperature_reached()
         m105 = Gcode({"message": "M105", "prot": g.prot})
         self.printer.processor.execute(m105)
         print all_ok
         if not False in all_ok:
             self._reply(m105)
             return 
         else:
             answer = m105.get_answer()
             answer += " E: "+ ("0" if self.printer.current_tool == "E" else "1")
             m105.set_answer(answer[2:]) # strip away the "ok"
             self._reply(m105)
             time.sleep(1)
예제 #17
0
 def get_message(self):
     """ Loop that gets messages and pushes them on the queue """
     while self.running:
         ret = select.select([self.tty], [], [], 1.0)
         if ret[0] == [self.tty]:
             message = self.tty.readline().strip("\n")
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": "USB"})
                 self.printer.processor.enqueue(g)
예제 #18
0
 def get_message(self):
     """ Loop that gets messages and pushes them on the queue """
     while self.running:
         r, w, x = select.select([self.rd], [], [], 1.0)
         if r:
             message = self.rd.readline().rstrip()
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": self.prot})
                 self.printer.processor.enqueue(g)
예제 #19
0
    def execute(self, g):
        if g.has_letter("P"):  # Load point
            index = int(g.get_value_by_letter("P"))
            point = self.printer.probe_points[index]
        else:
            # If no porobe point is specified, use current pos
            point = self.printer.path_planner.get_current_pos()
        if g.has_letter("X"):  # Override X
            point["X"] = float(g.get_value_by_letter("X"))
        if g.has_letter("Y"):  # Override Y
            point["Y"] = float(g.get_value_by_letter("Y"))

        probe_length = 0.01

        G0 = Gcode({
            "message": "G0 X{} Y{}".format(point["X"], point["Y"]),
            "prot": g.prot
        })
        self.printer.processor.execute(G0)
        self.printer.path_planner.wait_until_done()
        remaining_z = self.printer.path_planner.probe(
            probe_length)  # Probe one cm. TODO: get this from config
        probe_diff = probe_length - remaining_z
        # Update the current Z-position
        logging.info("G92 Z{:.10}".format(-probe_diff * 1000.0))
        G92 = Gcode({
            "message": "G92 Z{:.10}".format(-probe_diff * 1000.0),
            "prot": g.prot
        })
        self.printer.processor.execute(G92)

        logging.info("Found Z probe height {} at (X, Y) = ({}, {})".format(
            probe_diff, point["X"], point["Y"]))
        if g.has_letter("S"):
            if not g.has_letter("P"):
                logging.warning(
                    "G30: S-parameter was set, but no index (P) was set.")
            else:
                self.printer.probe_heights[index] = probe_diff
                self.printer.send_message(
                    g.prot,
                    "Found Z probe height {} at (X, Y) = ({}, {})".format(
                        probe_diff, point["X"], point["Y"]))
예제 #20
0
    def _make_buffered_queue_wait_for_async_queue(self):
        logging.info("queueing buffered-to-async sync event")
        event = Event()
        callback = SyncCallback(event)

        self.printer.path_planner.native_planner.queueSyncEvent(
            callback, False)

        buffered = Sync.BufferedWaitEvent(self.printer, event)
        buffered_gcode = Gcode({
            "message": "SyncBufferedToAsync_Buffered",
            "prot": "internal"
        })
        buffered_gcode.command = buffered

        # buffered doesn't actually use this, but we need it to stay alive per the garbage collector
        buffered.native_callback = callback

        self.printer.commands.put(buffered_gcode)
        logging.info("done queueing buffered-to-async sync event")
예제 #21
0
파일: USB.py 프로젝트: tomstokes/redeem
 def get_message(self):
     """ Loop that gets messages and pushes them on the queue """
     while self.running:
         ret = select.select([self.tty], [], [], 1.0)
         if ret[0] == [self.tty]:
             message = self.tty.readline().strip("\n")
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": "USB"})
                 if self.printer.processor.is_buffered(g):
                     self.printer.commands.put(g)
                 else:
                     self.printer.unbuffered_commands.put(g)
예제 #22
0
파일: Redeem.py 프로젝트: zittix/redeem
 def loop(self):
     try:
         while self.running:
             try:
                 gcode = Gcode(self.commands.get(True, 1.0))
             except Queue.Empty as e:
                 continue
             self._execute(gcode)
             self._reply(gcode)
             self.commands.task_done()
     except Exception as e:
         logging.exception("Ooops: ")
예제 #23
0
파일: USB.py 프로젝트: fca1/redeem-1
 def get_message(self):
     """ Loop that gets messages and pushes them on the queue """
     while self.running:
         ret = select.select([self.tty], [], [], 1.0)
         if ret[0] == [self.tty]:
             message = self.tty.readline().strip("\n")
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": "USB"})
                 self.printer.processor.enqueue(g)
                 # Do not enable sending messages until a
                 # message has been received
                 self.send_response = True
예제 #24
0
 def get_message(self):
     """ Loop that gets messages and pushes them on the queue """
     while self.running:
         r, w, x = select.select([self.rd], [], [], 1.0)
         if r:
             try:
                 message = self.rd.readline().rstrip()
                 if len(message) > 0:
                     g = Gcode({"message": message, "prot": self.prot})
                     self.printer.processor.enqueue(g)
             except IOError:
                 logging.warning("Could not read from pipe")
예제 #25
0
 def execute(self, g):
     all_ok = [False, False, False]
     while True:
         all_ok[0] |= self.printer.heaters[
             'E'].is_target_temperature_reached()
         all_ok[1] |= self.printer.heaters[
             'H'].is_target_temperature_reached()
         all_ok[2] |= self.printer.heaters[
             'HBP'].is_target_temperature_reached()
         m105 = Gcode({"message": "M105", "prot": g.prot})
         self.printer.processor.execute(m105)
         print all_ok
         if not False in all_ok:
             self._reply(m105)
             return
         else:
             answer = m105.get_answer()
             answer += " E: " + ("0" if self.printer.current_tool == "E"
                                 else "1")
             m105.set_answer(answer[2:])  # strip away the "ok"
             self._reply(m105)
             time.sleep(1)
예제 #26
0
    def execute(self, g):
        num_factors = g.get_int_by_letter("F", 4)
        if num_factors < 3 or num_factors > 4:
            logging.error("G33: Invalid number of calibration factors.")

        # we reuse the G29 macro for the autocalibration purposes
        gcodes = self.printer.config.get("Macros", "G29").split("\n")
        self.printer.path_planner.wait_until_done()
        for gcode in gcodes:        
            G = Gcode({"message": gcode, "prot": g.prot})
            self.printer.processor.execute(G)
            self.printer.path_planner.wait_until_done()

        # adjust probe heights
        probe_z_coords = np.array(self.printer.probe_heights[:len(self.printer.probe_points)])
        offset_z = self.printer.config.getfloat('Probe', 'offset_z')*1000.
        logging.info("adjusting by " + str(offset_z))
        # this is where the print head was when the probe was triggered
        print_head_zs = probe_z_coords - offset_z

        # Log the found heights
        logging.info("Found heights: "+str(np.round(print_head_zs, 2)))

        simulate_only = g.has_letter("S")

        # run the actual delta autocalibration
        params = self.printer.path_planner.autocalibrate_delta_printer(
                    num_factors, simulate_only,
                    self.printer.probe_points, print_head_zs)
        logging.info("Finished printer autocalibration\n")

        if g.has_letter("P"):
            # dump the dictionary to log file
            logging.debug(str(params)) 
            
            #pretty print to printer output
            self.printer.send_message(g.prot, "delta calibration : L = %g"%params["L"])
            self.printer.send_message(g.prot, "delta calibration : r = %g"%params["r"])
            self.printer.send_message(g.prot, "delta calibration : A_tangential = %g"%params["A_tangential"])
            self.printer.send_message(g.prot, "delta calibration : B_tangential = %g"%params["B_tangential"])
            self.printer.send_message(g.prot, "delta calibration : C_tangential = %g"%params["C_tangential"])
            self.printer.send_message(g.prot, "delta calibration : offset_x = %g"%params["offset_x"])
            self.printer.send_message(g.prot, "delta calibration : offset_y = %g"%params["offset_y"])
            self.printer.send_message(g.prot, "delta calibration : offset_z = %g"%params["offset_z"])
        
        return
예제 #27
0
 def get_message(self):
     """Loop that gets messages and pushes them on the queue"""
     while self.running:
         #logging.info("Ethernet listening")
         self.s.settimeout(1.0)
         try:
             self.client, self.address = self.s.accept()
         except IOError as e:
             continue
         logging.info("Ethernet connection accepted")
         self.s.settimeout(1.0)
         while self.running:
             line = self.read_line()
             if line is None:
                 break
             message = line.strip("\n")
             if len(message) > 0:
                 g = Gcode({"message": message, "prot": "Eth"})
                 self.printer.processor.enqueue(g)
예제 #28
0
    def execute(self, g):

        gcodes = self.printer.config.get("Macros", "G29").split("\n")
        self.printer.path_planner.wait_until_done()
        for gcode in gcodes:
            G = Gcode({"message": gcode, "prot": g.prot})
            self.printer.processor.execute(G)
            self.printer.path_planner.wait_until_done()

        # Remove the offset from the probed points
        self.printer.probe_heights -= min(self.printer.probe_heights)

        # Log the found heights
        for k, v in enumerate(self.printer.probe_points):
            self.printer.probe_points[k]["Z"] = self.printer.probe_heights[k]
        logging.info("Found heights: ")
        logging.info(self.printer.probe_points)

        # Update the bed compensation matrix
        Path.update_autolevel_matrix(self.printer.probe_points,
                                     self.printer.probe_heights)
예제 #29
0
파일: Autotune.py 프로젝트: Sciumo/redeem
 def send_temperature(self):
     m105 = Gcode({"message": "M105", "prot": self.g.prot})
     self.printer.processor.execute(m105)
     answer = m105.get_answer()
     m105.set_answer(answer[2:])  # strip away the "ok"
     self.printer.reply(m105)
예제 #30
0
 def send_temperature(self):
     m105 = Gcode({"message": "M105", "prot": self.g.prot})
     self.printer.processor.execute(m105)
     answer = m105.get_answer()
     m105.set_answer(answer[2:])  # strip away the "ok"
     self.printer.reply(m105)
예제 #31
0
 def get_test_gcodes(self):
     gcodes = []
     for name, gcode in iteritems(self.gcodes):
         for str in gcode.get_test_gcodes():
             gcodes.append(Gcode({"message": str, "prot": "Test"}))
     return gcodes
예제 #32
0
    def execute(self, g):
        if g.has_letter("P"):  # Load point
            index = int(g.get_value_by_letter("P"))
            point = self.printer.probe_points[index]
        else:
            # If no porobe point is specified, use current pos
            point = self.printer.path_planner.get_current_pos()
        if g.has_letter("X"):  # Override X
            point["X"] = float(g.get_value_by_letter("X"))
        if g.has_letter("Y"):  # Override Y
            point["Y"] = float(g.get_value_by_letter("Y"))
        if g.has_letter("Z"):  # Override Z
            point["Z"] = float(g.get_value_by_letter("Z"))

        # Get probe length, if present, else use 1 cm.
        if g.has_letter("D"):
            probe_length = float(g.get_value_by_letter("D"))
        else:
            probe_length = self.printer.config.getfloat('Probe', 'length')

        # Get probe speed. If not preset, use printers curent speed.
        if g.has_letter("F"):
            probe_speed = float(g.get_value_by_letter("F")) / 60000.0
        else:
            probe_speed = self.printer.config.getfloat('Probe', 'length')

        # Get acceleration. If not present, use value from config.
        if g.has_letter("A"):
            probe_accel = float(g.get_value_by_letter("A"))
        else:
            probe_accel = self.printer.config.getfloat('Probe', 'accel')

        # Find the Probe offset
        offset_x = self.printer.config.getfloat('Probe', 'offset_x') * 1000
        offset_y = self.printer.config.getfloat('Probe', 'offset_y') * 1000

        # Move to the position
        G0 = Gcode({
            "message":
            "G0 X{} Y{} Z{}".format(point["X"] + offset_x,
                                    point["Y"] + offset_y, point["Z"]),
            "prot":
            g.prot
        })
        self.printer.processor.execute(G0)
        self.printer.path_planner.wait_until_done()
        bed_dist = self.printer.path_planner.probe(
            probe_length, probe_speed,
            probe_accel)  # Probe one cm. TODO: get this from config
        logging.debug("Bed dist: " + str(bed_dist * 1000) + " mm")

        # Add the probe offsets to the points

        #logging.info("Found Z probe height {} at (X, Y) = ({}, {})".format(bed_dist, point["X"], point["Y"]))
        if g.has_letter("S"):
            if not g.has_letter("P"):
                logging.warning(
                    "G30: S-parameter was set, but no index (P) was set.")
            else:
                self.printer.probe_heights[index] = bed_dist
                self.printer.send_message(
                    g.prot,
                    "Found Z probe height {} at (X, Y) = ({}, {})".format(
                        bed_dist, point["X"], point["Y"]))
예제 #33
0
파일: G30.py 프로젝트: fca1/redeem-1
    def execute(self, g):
        if g.has_letter("P"):  # Load point
            index = int(g.get_value_by_letter("P"))
            point = self.printer.probe_points[index]
        else:
            # If no probe point is specified, use current pos
            # this value is in metres
            # need to convert to millimetres as we are using
            #this value for a G0 call
            point = self.printer.path_planner.get_current_pos(mm=True,
                                                              ideal=True)
            logging.debug("G30: current position (mm) :  X{} Y{} Z{}".format(
                point["X"], point["Y"], point["Z"]))

        if g.has_letter("X"):  # Override X
            point["X"] = float(g.get_value_by_letter("X"))
        if g.has_letter("Y"):  # Override Y
            point["Y"] = float(g.get_value_by_letter("Y"))
        if g.has_letter("Z"):  # Override Z
            point["Z"] = float(g.get_value_by_letter("Z"))

        # Get probe length, if present, else use 1 cm.
        if g.has_letter("D"):
            probe_length = float(g.get_value_by_letter("D")) / 1000.
        else:
            probe_length = self.printer.config.getfloat('Probe', 'length')

        # Get probe speed. If not preset, use printers curent speed.
        if g.has_letter("F"):
            probe_speed = float(g.get_value_by_letter("F")) / 60000.0
        else:
            probe_speed = self.printer.config.getfloat('Probe', 'speed')

        # Get acceleration. If not present, use value from config.
        if g.has_letter("A"):
            probe_accel = float(g.get_value_by_letter("A"))
        else:
            probe_accel = self.printer.config.getfloat('Probe', 'accel')

        use_bed_matrix = bool(g.get_int_by_letter("B", 0))

        # Find the Probe offset
        # values in config file are in metres, need to convert to millimetres
        offset_x = self.printer.config.getfloat('Probe', 'offset_x') * 1000
        offset_y = self.printer.config.getfloat('Probe', 'offset_y') * 1000

        logging.debug("G30: probing from point (mm) : X{} Y{} Z{}".format(
            point["X"] + offset_x, point["Y"] + offset_y, point["Z"]))

        # Move to the position
        G0 = Gcode({
            "message":
            "G0 X{} Y{} Z{}".format(point["X"] + offset_x,
                                    point["Y"] + offset_y, point["Z"]),
            "prot":
            g.prot
        })
        self.printer.processor.execute(G0)
        self.printer.path_planner.wait_until_done()
        bed_dist = self.printer.path_planner.probe(
            probe_length, probe_speed, probe_accel) * 1000.0  # convert to mm
        logging.debug("Bed dist: " + str(bed_dist) + " mm")

        self.printer.send_message(
            g.prot,
            "Found Z probe distance {0:.2f} mm at (X, Y) = ({1:.2f}, {2:.2f})".
            format(bed_dist, point["X"], point["Y"]))

        Alarm.action_command("bed_probe_point",
                             json.dumps([point["X"], point["Y"], bed_dist]))

        # Must have S to save the probe bed distance
        # this is required for calculation of the bed compensation matrix
        # NOTE: the use of S in G30 is different to that in G29, here "S" means "save"
        if g.has_letter("S"):
            if not g.has_letter("P"):
                logging.warning(
                    "G30: S-parameter was set, but no index (P) was set.")
            else:
                self.printer.probe_heights[index] = bed_dist