Exemple #1
0
 def setUp(self):
     packet = {
         "message":
         "N99  G2 8 x0 Y-0.2345 z +12.345 67 ab C(comment)*92; noise",
         "answer": "ok"
     }
     self.g = Gcode(packet)
     self.g.printer.unit_factor = 1.0
Exemple #2
0
    def test_gcode_parser(self):  # Gocde::__init__
        self.assertEqual(self.g.gcode, "G28")
        self.assertEqual(self.g.tokens,
                         ["X0", "Y-0.2345", "Z+12.34567", "A", "B", "C"])

        packet = {"message": "; noise"}
        g = Gcode(packet)
        self.assertEqual(g.code(), "No-Gcode")
Exemple #3
0
  def execute(self, g):
    self.printer.running_M116 = True

    # heater_index:
    # -1 - HBP, 0 - E, 1 - H, 2 - A, 3 - B, 4 - C
    # No P or H Parameter means all temperatures must be reached

    has_parameter = g.has_letter("P") or g.has_letter("T")
    if has_parameter:
      if g.has_letter("P"):    # Set hotend temp based on the P-param
        heater_index = g.get_int_by_letter("P", 0)
      elif g.has_letter("T"):    # Set hotend temp based on the T-param
        heater_index = g.get_int_by_letter("T", 0)
      if heater_index > len(self.printer.heaters) - 1:
        logging.warning("M116: heater index out of bounds: {}".format(heater_index))
        return

    all_ok = [
        has_parameter and heater_index != 0, has_parameter and heater_index != 1, has_parameter
        and heater_index != -1
    ]
    if self.printer.config.reach_revision:
      all_ok.extend([
          has_parameter and heater_index != 2, has_parameter and heater_index != 3, has_parameter
          and heater_index != 4
      ])

    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()

      if self.printer.config.reach_revision:
        all_ok[3] |= self.printer.heaters['A'].is_target_temperature_reached()
        all_ok[4] |= self.printer.heaters['B'].is_target_temperature_reached()
        all_ok[5] |= self.printer.heaters['C'].is_target_temperature_reached()

      m105 = Gcode({"message": "M105", "parent": g})
      self.printer.processor.resolve(m105)
      self.printer.processor.execute(m105)
      if False not in all_ok or not self.printer.running_M116:
        logging.info("Heating done.")
        self.printer.send_message(g.prot, "Heating done.")
        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)
Exemple #4
0
    def test_gcodes_M105(self):
        test_temps = {}
        test_target_temps = {}
        test_powers = {}
        for n in self.printer.heaters:
            prefix = self.printer.heaters[n].prefix
            test_temps[prefix] = round(random() * 100, 2)
            test_target_temps[prefix] = round(random() * 100, 2)
            test_powers[prefix] = round(random(), 2)
            self.printer.heaters[n].get_temperature = mock.Mock(
                return_value=test_temps[prefix])
            self.printer.heaters[n].get_target_temperature = mock.Mock(
                return_value=test_target_temps[prefix])
            self.printer.heaters[n].mosfet = mock.Mock(
            )  # TODO why is this necessary? All the heaters seem to be sharing the same mosfet?
            self.printer.heaters[n].mosfet.get_power = mock.Mock(
                return_value=test_powers[prefix])

        g = Gcode({"message": "M105"})
        self.printer.processor.gcodes[g.gcode].execute(g)

        expected_answer = "ok"
        expected_answer += " {0}:{1:.1f}/{2:.1f}".format(
            "T", test_temps["T0"], test_target_temps["T0"])

        for heater, data in sorted(iteritems(self.printer.heaters),
                                   key=lambda (k, v): (v, k)):
            p = data.prefix
            expected_answer += " {0}:{1:.1f}/{2:.1f}".format(
                p, test_temps[p], test_target_temps[p])
        expected_answer += " @:{0:.1f}".format(
            math.floor(test_powers["T0"] * 255.0))

        self.assertEqual(g.answer, expected_answer)
Exemple #5
0
 def test_gcodes_M114(self):
   A = round(random() * 200, 1)
   B = round(random() * 200, 1)
   C = round(random() * 200, 1)
   X = round(random() * 200, 1)
   Y = round(random() * 200, 1)
   Z = round(random() * 200, 1)
   E = round(random() * 200, 1)
   H = round(random() * 200, 1)
   self.printer.path_planner.get_current_pos = mock.Mock(return_value={
       'A': A,
       'C': C,
       'B': B,
       'E': E,
       'H': H,
       'Y': Y,
       'X': X,
       'Z': Z
   })
   g = Gcode({"message": "M114"})
   self.printer.processor.gcodes[g.gcode].execute(g)
   self.printer.path_planner.get_current_pos.assert_called_with(
       ideal=True, mm=True)    # kinda redundant, but hey.
   self.assertEqual(
       g.answer,
       "ok C: X:{:.1f} Y:{:.1f} Z:{:.1f} E:{:.1f} A:{:.1f} B:{:.1f} C:{:.1f} H:{:.1f}".format(
           X, Y, Z, E, A, B, C, H))
Exemple #6
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:
      # If 'S' (imulate) remove M561 and M500 codes
      if (g.has_letter("S")) and ("RFS" in gcode):
        logging.debug("G29: Removing due to RFS: " + str(gcode))
      else:    # Execute the code
        G = Gcode({"message": gcode, "parent": g})
        self.printer.processor.resolve(G)
        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.config.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))
Exemple #7
0
 def assertGcodeProperties(self, gcode, is_buffered=False, is_async=False):
     gcode_instance = Gcode({"message": gcode})
     self.printer.processor.resolve(gcode_instance)
     gcode_handler = gcode_instance.command
     self.assertNotEqual(gcode_handler.get_description(), "")
     self.assertNotEqual(gcode_handler.get_long_description(), "")
     self.assertEqual(gcode_handler.is_buffered(), is_buffered)
     self.assertEqual(gcode_handler.is_async(), is_async)
Exemple #8
0
 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, "parent": g})
         self.printer.processor.resolve(G)
         self.printer.processor.execute(G)
         self.printer.path_planner.wait_until_done()
Exemple #9
0
 def execute(self, g):
   temperature = g.get_float_by_letter("S")
   self.printer.heaters['HBP'].set_target_temperature(temperature)
   G = Gcode({
       "message": "M116 P-1",    # P-1 = HBP
       "parent": g
   })
   self.printer.processor.resolve(G)
   self.printer.processor.execute(G)
Exemple #10
0
def test_code(test_str):
    r = Redeem("/usr/src/redeem/configs")
    r.printer.enable.set_enabled()
    g = r.printer.processor
    for line in test_str.splitlines():
        if line:
            print line
            g.execute(Gcode({"message": line, "prot": "testing"}))

    r.exit()
Exemple #11
0
    def execute(self, g):
        m104 = Gcode({
            "message": "M104 " + " ".join(g.get_tokens()),
            "parent": g
        })
        self.printer.processor.resolve(m104)
        self.printer.processor.execute(m104)

        has_parameter = g.has_letter("P") or g.has_letter("T")
        if not has_parameter:
            heaters = ["E", "H"]
            if self.printer.config.reach_revision:
                heaters.extend(["A", "B", "C"])
            parameters = ["P" + str(heaters.index(self.printer.current_tool))]
        else:
            parameters = g.get_tokens()

        m116 = Gcode({"message": "M116 " + " ".join(parameters), "parent": g})
        self.printer.processor.resolve(m116)
        self.printer.processor.execute(m116)
Exemple #12
0
def test_file(test):
    r = Redeem("/usr/src/redeem/configs")
    r.printer.enable.set_enabled()
    g = r.printer.processor
    f = open(test, 'r')
    for line in f.readlines():
        if line:
            print line
            g.execute(Gcode({"message": line, "prot": "testing"}))

    r.exit()
    f.close()
Exemple #13
0
def test4():
    test_str = """
        G28
        G1 F5000.000
        G1 Z0.350
        G1 X-100.0 Y-100.0
        G1 X100.0 Y-100.0
        G1 X100.0 Y100.0
        G1 X-100.0 Y100.0
        G1 X0.0 Y0.0
        """
    r = Redeem()
    g = r.printer.processor
    for line in test_str.splitlines():
        if line:
            print line
            g.execute(Gcode({"message": line, "prot": "testing"}))

    r.exit()
Exemple #14
0
    def test_gcodes_M115(self):

        g = Gcode({"message": "M115"})
        self.printer.processor.gcodes[g.gcode].execute(g)

        self.assertRegexpMatches(g.answer, "PROTOCOL_VERSION:\S+")
        self.assertRegexpMatches(g.answer, "REPLICAPE_KEY:TESTING_DUMMY_KEY")
        self.assertRegexpMatches(g.answer, "FIRMWARE_NAME:Redeem")
        self.assertRegexpMatches(
            g.answer,
            "FIRMWARE_VERSION:{}\s".format(re.escape(__long_version__)))
        self.assertRegexpMatches(g.answer, "FIRMWARE_URL:https:\S+")
        self.assertRegexpMatches(
            g.answer, "MACHINE_TYPE:{}\s".format(
                re.escape(self.printer.config.get('System', 'machine_type'))))
        self.assertRegexpMatches(
            g.answer, "EXTRUDER_COUNT:{}".format(self.printer.NUM_EXTRUDERS))
        self.assertRegexpMatches(
            g.answer,
            "DISTRIBUTION_NAME:halloween DISTRIBUTION_VERSION:bumpkins")
Exemple #15
0
    def process_gcode(self, g):
        profile = cProfile.Profile()
        self.printer.sd_card_manager.set_status(True)
        profile.enable()
        for line in self.printer.sd_card_manager:
            line = line.strip()
            if not line or line.startswith(';'):
                continue
            file_g = Gcode({"message": line})
            self.printer.processor.enqueue(file_g)
        if self.printer.sd_card_manager.get_status():
            logging.info("M24: Print from file complete")
        self.printer.sd_card_manager.set_status(False)

        self.printer.send_message(g.prot, "Done printing file")
        profile.disable()
        s = StringIO.StringIO()
        sortby = 'cumulative'
        ps = pstats.Stats(profile, stream=s).sort_stats(sortby)
        ps.print_stats()
        logging.debug(s.getvalue())
        self.printer.sd_card_manager.reset()
Exemple #16
0
 def test_gcode_is_info_command(self):
     self.assertEqual(self.g.is_info_command(), False)
     g = Gcode({"message": "G28?"})
     self.assertEqual(g.is_info_command(), True)
Exemple #17
0
 def exec_and_wait(cmd):
     G = Gcode({"message": cmd, "prot": g.prot})
     self.printer.processor.execute(G)
     self.printer.path_planner.wait_until_done()
Exemple #18
0
  def execute(self, g):
    # Catch most letters to tell user use may have been in error.
    if g.has_letter("P"):
      self.printer.send_message(
          g.prot, "Warning: P not supported for G30.1, proceeding as if none existed.")
    if g.has_letter("X"):    # Override X
      self.printer.send_message(
          g.prot, "Warning: X not supported for G30.1, proceeding as if none existed.")
    if g.has_letter("Y"):    # Override Y
      self.printer.send_message(
          g.prot, "Warning: Y not supported for G30.1, proceeding as if none existed.")
    if g.has_letter("Z"):    # Override Z
      Z_new = g.get_float_by_letter("Z")
    else:
      Z_new = 0

    # Usable letters listed here
    # Get probe length, if present, else use value from config.
    if g.has_letter("D"):
      probe_length = g.get_float_by_letter("D") / 1000.
    else:
      probe_length = self.printer.config.getfloat('Probe', 'length')

    # Get probe speed. If not preset, use printer's current speed.
    if g.has_letter("F"):
      probe_speed = g.get_float_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("Q"):
      probe_accel = g.get_float_by_letter("Q") / 3600000.0
    else:
      probe_accel = self.printer.config.getfloat('Probe', 'accel')

    point = self.printer.path_planner.get_current_pos(mm=True, ideal=True)
    logging.debug("G30.1: current position (mm) :  X{} Y{} Z{}".format(
        point["X"], point["Y"], point["Z"]))
    logging.debug("G30.1: will set bed to Z{}".format(Z_new))

    # 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
    offset_z = self.printer.config.getfloat('Probe', 'offset_z') * 1000

    logging.debug("G30.1: 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"]),
        "parent":
            g
    })
    self.printer.processor.resolve(G0)
    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.info("G30: adjusting offset using Z-probe offset by " + str(offset_z))
    bed_dist = bed_dist - offset_z    # apply z offset

    # calculated required offset to make bed equal to Z0 or user's specified Z.
    # should be correct, assuming probe starts at Z(5), requested Z(0)  probe Z(-0.3), adjusted global Z should be 5.3
    # assuming probe starts at Z(5), and probe result is -0.3. This says real probe start was 5.3.
    # if we want to start printing at 0.2 above bed, new z should be 5.1.
    # 5 - (-0.3) - 0.2 = 5.1

    Z_adjusted = point["Z"] - bed_dist - Z_new
    logging.debug("Bed dist: " + str(bed_dist) + " mm")
    logging.debug("Old Z{}, New Z{}.".format(point["Z"], Z_adjusted))

    self.printer.send_message(g.prot, "Offsetting Z by {} to Z{}".format(bed_dist, Z_adjusted))
    # wiley's advice on bypassing sending via G92
    pos = {}
    pos["Z"] = Z_adjusted / 1000.0
    path = G92Path(pos, self.printer.feed_rate)
    self.printer.path_planner.add_path(path)

    # not sure if next part is necessary
    Alarm.action_command("bed_probe_point", json.dumps([point["X"], point["Y"], bed_dist]))
Exemple #19
0
 def test_gcode_set_tokens(self):
     g = Gcode({"message": "G28"})
     g.set_tokens(["G28", "X0", "Y0"])
     self.assertEqual(g.tokens, ["G28", "X0", "Y0"])
Exemple #20
0
    def test_gcode_is_valid(self):
        self.assertEqual(self.g.is_valid(), True)

        packet = {"message": "N99  G28*60; noise"}  # invalid checksum
        g = Gcode(packet)
        self.assertEqual(g.is_valid(), False)
Exemple #21
0
  def execute(self, g):
    if g.has_letter("P"):    # Load point
      index = g.get_int_by_letter("P")
      try:
        point = self.printer.probe_points[index]
      except IndexError:
        logging.warning("G30 point P%d not yet defined. Aborting.", index)
        return
    else:
      # If no probe point is specified, use current position
      # this value is in metres
      # 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"]))
    """ do not convert to SI m because used as is in gcode command, below """
    if g.has_letter("X"):    # Override X
      point["X"] = g.get_float_by_letter("X")
    if g.has_letter("Y"):    # Override Y
      point["Y"] = g.get_float_by_letter("Y")
    if g.has_letter("Z"):    # Override Z
      point["Z"] = g.get_float_by_letter("Z")

    # Get probe length, if present, else use value from config.
    if g.has_letter("D"):
      probe_length = g.get_float_by_letter("D") / 1000.
    else:
      probe_length = self.printer.config.getfloat('Probe', 'length')

    # Get probe speed, if present, else use value from config.
    if g.has_letter("F"):
      probe_speed = g.get_float_by_letter("F") / 60000.    # m/s
    else:
      probe_speed = self.printer.config.getfloat('Probe', 'speed')

    # Get acceleration, if present, else use value from config.
    if g.has_letter("Q"):
      probe_accel = g.get_float_by_letter("Q") / 3600000.    # m/s^2
    else:
      probe_accel = self.printer.config.getfloat('Probe', 'accel')

    # 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
    offset_z = self.printer.config.getfloat('Probe', 'offset_z') * 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"]),
        "parent":
            g
    })
    self.printer.processor.resolve(G0)
    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.info("G30: adjusting offset using Z-probe offset by " + str(offset_z))
    bed_dist = bed_dist - offset_z    # apply z offset
    bed_dist = round(bed_dist, 3)

    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 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
Exemple #22
0
def test2():
    r = Redeem()
    g = r.printer.processor
    g.execute(Gcode({"message": "G28", "prot": "testing"}))
    r.exit()
Exemple #23
0
    def execute(self, g):
        num_factors = g.get_int_by_letter("N", 4)
        if num_factors not in [3, 4, 6, 8, 9]:
            msg = "G33: Invalid number of calibration factors."
            logging.error(msg)
            self.printer.send_message(g.prot, msg)
            return

        # 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, "parent": g})
            self.printer.processor.resolve(G)
            self.printer.processor.execute(G)
            self.printer.path_planner.wait_until_done()

        # adjust probe heights
        print_head_zs = np.array(
            self.printer.probe_heights[:len(self.printer.probe_points)])

        # Log the found heights
        logging.info("G33: 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("G33: 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_angular = %g" % params["A_angular"])
            self.printer.send_message(
                g.prot,
                "delta calibration : B_angular = %g" % params["B_angular"])
            self.printer.send_message(
                g.prot,
                "delta calibration : C_angular = %g" % params["C_angular"])
            self.printer.send_message(
                g.prot,
                "delta calibration : A_radial = %g" % params["A_radial"])
            self.printer.send_message(
                g.prot,
                "delta calibration : B_radial = %g" % params["B_radial"])
            self.printer.send_message(
                g.prot,
                "delta calibration : C_radial = %g" % params["C_radial"])
            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
Exemple #24
0
 def execute_gcode(cls, text):
     g = Gcode({"message": text})
     g.prot = 'testing_noret'
     cls.printer.processor.resolve(g)
     g.command.execute(g)
     return g
Exemple #25
0
 def test_gcode_is_crc(self):
     self.assertEqual(self.g.is_crc(), True)
     g = Gcode({"message": "G28"})
     self.assertEqual(g.is_crc(), False)
Exemple #26
0
class GcodeTest(MockPrinter):
    def setUp(self):
        packet = {
            "message":
            "N99  G2 8 x0 Y-0.2345 z +12.345 67 ab C(comment)*92; noise",
            "answer": "ok"
        }
        self.g = Gcode(packet)
        self.g.printer.unit_factor = 1.0

    def tearDown(self):
        self.g = None
        return

    def test_gcode_parser(self):  # Gocde::__init__
        self.assertEqual(self.g.gcode, "G28")
        self.assertEqual(self.g.tokens,
                         ["X0", "Y-0.2345", "Z+12.34567", "A", "B", "C"])

        packet = {"message": "; noise"}
        g = Gcode(packet)
        self.assertEqual(g.code(), "No-Gcode")

    def test_gcode_code(self):
        self.assertEqual(self.g.code(), "G28")

    def test_gcode_is_valid(self):
        self.assertEqual(self.g.is_valid(), True)

        packet = {"message": "N99  G28*60; noise"}  # invalid checksum
        g = Gcode(packet)
        self.assertEqual(g.is_valid(), False)

    def test_gcode_token_letter(self):
        self.assertEqual(self.g.token_letter(2), "Z")

    def test_token_value(self):
        self.assertEqual(self.g.token_value(1), -0.2345)

    def test_gcode_token_distance(self):
        self.assertEqual(self.g.token_distance(2), 12.34567)

        self.g.printer.unit_factor = 2.54
        self.assertEqual(self.g.token_distance(2), 12.34567 * 2.54)

    def test_gcode_get_tokens(self):
        self.assertEqual(self.g.get_tokens(), self.g.tokens)

    def test_gcode_set_tokens(self):
        g = Gcode({"message": "G28"})
        g.set_tokens(["G28", "X0", "Y0"])
        self.assertEqual(g.tokens, ["G28", "X0", "Y0"])

    def test_gcode_get_message(self):
        """ should return message with line number, checksum, gcode (comments) and non-standrd ;comments removed """
        self.assertEqual(self.g.get_message(),
                         "G2 8 x0 Y-0.2345 z +12.345 67 ab C")

    def test_gcode_has_letter(self):
        self.assertEqual(self.g.has_letter("Y"), True)
        self.assertEqual(self.g.has_letter("W"), False)
        self.assertEqual(self.g.has_letter("N"), False)
        self.assertEqual(self.g.has_letter("W"), False)
        self.assertEqual(self.g.has_letter("B"), True)
        self.assertEqual(self.g.has_letter("C"), True)

    def test_gcode_has_value(self):
        self.assertEqual(self.g.has_value(0), True)
        self.assertEqual(self.g.has_value(1), True)
        self.assertEqual(self.g.has_value(2), True)
        self.assertEqual(self.g.has_value(3), False)
        self.assertEqual(self.g.has_value(4), False)
        self.assertEqual(self.g.has_value(5), False)

    def test_gcode_get_token_index_by_letter(self):
        self.assertEqual(self.g.get_token_index_by_letter("X"), 0)
        self.assertEqual(self.g.get_token_index_by_letter("Y"), 1)
        self.assertEqual(self.g.get_token_index_by_letter("C"), 5)

    def test_gcode_get_float_by_letter(self):
        self.assertEqual(self.g.get_float_by_letter("Y"), -0.2345)

    def test_gcode_get_distance_by_letter(self):
        self.g.printer.unit_factor = 2.54
        self.assertEqual(self.g.get_distance_by_letter("Y"), -0.2345 * 2.54)

    def test_gcode_get_int_by_letter(self):
        self.assertEqual(self.g.get_int_by_letter("Z"), 12)

    def test_gcode_has_letter_value(self):
        self.assertEqual(self.g.has_letter_value("X"), True)
        self.assertEqual(self.g.has_letter_value("A"), False)

    def test_gcode_remove_token_by_letter(self):
        self.g.remove_token_by_letter("Z")
        self.assertEqual(self.g.tokens, ["X0", "Y-0.2345", "A", "B", "C"])

    def test_gcode_num_tokens(self):
        self.assertEqual(self.g.num_tokens(), 6)

    def test_gcode_get_tokens_as_dict(self):
        t = self.g.get_tokens_as_dict()
        keys = ["A", "B", "C", "X", "Y", "Z"]
        vals = [0.0, 0.0, 0.0, 0.0, -0.2345, 12.34567]
        for i, key in enumerate(sorted(t)):
            self.assertEqual(key, keys[i])
            self.assertEqual(t[key], vals[i])

    def test_gcode_get_cs(self):
        self.assertEqual(1, self.g._getCS("1234567890"))

    def test_gcode_is_crc(self):
        self.assertEqual(self.g.is_crc(), True)
        g = Gcode({"message": "G28"})
        self.assertEqual(g.is_crc(), False)

    def test_gcode_get_answer(self):
        self.assertEqual(self.g.get_answer(), "ok")

    def test_gcode_set_answer(self):
        self.assertEqual(self.g.answer, "ok")
        self.g.set_answer("xxx")
        self.assertEqual(self.g.answer, "xxx")

    def test_gcode_is_info_command(self):
        self.assertEqual(self.g.is_info_command(), False)
        g = Gcode({"message": "G28?"})
        self.assertEqual(g.is_info_command(), True)