Exemplo n.º 1
0
    def test_handle_gcode_inch_switch(self, no_safe_robot):
        """
        Test that the inch mode switch works correctly.
        :param no_safe_robot:
        :return:
        """
        activate_inch = GCmd.read_cmd_str("G20")
        deactivate_inch = GCmd.read_cmd_str("G21")

        no_safe_robot.assign_task(activate_inch)
        sleep(0.1)
        assert no_safe_robot.inch_active

        no_safe_robot.assign_task(deactivate_inch)
        sleep(0.1)
        assert not no_safe_robot.inch_active
Exemplo n.º 2
0
def execute_gcode(ip: str, port: int, serial_ids: Tuple[int, int], filepath: str) -> None:
    """
    Executing G-Code commands from a file.
    :param ip:
    :param port:
    :param serial_ids:
    :param filepath:
    :return:
    """
    logging.info(f'Reading G-Code from "{filepath}".')
    with open(filepath, 'r') as f:
        cmd_raw = f.readlines()

    commands = [GCmd.read_cmd_str(cmd_str.strip()) for cmd_str in cmd_raw if not cmd_str.startswith(GCmd.COMMENT)]
    commands = [cmd for cmd in commands if cmd is not None]
    total_commands = len(commands)

    # Create printer object
    printer = GPrinter.default_init(ip, port, serial_ids=serial_ids, safe_return=False)

    # Executing communication
    try:
        for idx, cmd in enumerate(commands):
            logging.info(f'Executing next ({idx + 1}/{total_commands}): {cmd}')
            printer.execute(cmd)
    except MelfaBaseException as e:
        print(str(e))
    except KeyboardInterrupt:
        print('Program terminated by user.')
    finally:
        printer.shutdown()
Exemplo n.º 3
0
def interactive_gcode(ip: str, port: int, serial_ids: Tuple[int, int], safe_return: Optional[bool] = False) -> None:
    """
    Launches an interactive shell accepting G-code.
    :param ip: IPv4 network address of the robot
    :param port: Port of the robot
    :param serial_ids:
    :param safe_return:
    """
    logging.info("Launching interactive G-code shell...")

    # Create printer object
    printer = GPrinter.default_init(ip, port, serial_ids=serial_ids, safe_return=safe_return)

    # Executing communication
    try:
        while True:
            usr_msg = input("G-Code>")
            if usr_msg.lower() in ["quit"]:
                raise KeyboardInterrupt
            if len(usr_msg) > 0:
                # Parse G-code
                gcode = GCmd.read_cmd_str(usr_msg)
                print(str(gcode))
                printer.execute(gcode)
    except MelfaBaseException as e:
        print(str(e))
    except KeyboardInterrupt:
        print('Program terminated by user.')
    finally:
        printer.shutdown()
Exemplo n.º 4
0
 def test_execute(self, virtual_environ, cmd_str):
     """
     Test that commands are sent to all corresponding components
     :return:
     """
     printer, dummy_robot_ctrl = virtual_environ
     cmd = GCmd.read_cmd_str(cmd_str)
     printer.execute(cmd)
     printer.shutdown()
Exemplo n.º 5
0
    def test_str_comment(self):
        """
        Test that a comment is turned to None
        :return:
        """
        GCmd.DIGITS = 1
        GCmd.CMD_REMOVE_LEAD_ZERO = False

        out_act = GCmd.read_cmd_str("; I am a comment.")
        assert out_act is None
Exemplo n.º 6
0
    def test_str_convert_seconds(self):
        """
        Test that seconds are converted to milliseconds
        :return:
        """
        GCmd.DIGITS = 1
        GCmd.CMD_REMOVE_LEAD_ZERO = False

        out_act = str(GCmd.read_cmd_str("G01 X-50.0 Y30.0 F30.2 E17.1 S20.3"))
        assert out_act == "G01 X-50.0 Y30.0 F30.2 E17.1 P20300.0"
Exemplo n.º 7
0
    def test_str_g28_remove_coordinates(self):
        """
        Test that for G28 (homing) only the axes are considered
        :return:
        """
        GCmd.DIGITS = 1
        GCmd.CMD_REMOVE_LEAD_ZERO = False

        out_act = str(GCmd.read_cmd_str("G28 Y10.0 Z"))
        assert out_act == "G28 Y Z"
Exemplo n.º 8
0
    def test_str_remove_lead_zero(self):
        """
        Test that lead zeroes in the command can be removed
        :return:
        """
        GCmd.DIGITS = 1
        GCmd.CMD_REMOVE_LEAD_ZERO = True

        out_act = str(GCmd.read_cmd_str("G01 X10.3 Z-10.3"))
        assert out_act == "G1 X10.3 Z-10.3"
Exemplo n.º 9
0
    def test_str_recurrent(self, test_input):
        """
        Tests the creation of objects from a command string and their conversion back to the string.
        :return:
        """
        # Define digits to have outcomes as expected.
        GCmd.DIGITS = 1
        GCmd.CMD_REMOVE_LEAD_ZERO = False

        out_act = str(GCmd.read_cmd_str(test_input))
        assert out_act == test_input
Exemplo n.º 10
0
def interactive_gcode_robot_only(ip: str,
                                 port: int,
                                 safe_return: Optional[bool] = False) -> None:
    """
    Launches an interactive shell accepting G-code.
    :param ip: IPv4 network address of the robot
    :param port: Port of the robot
    :param safe_return:
    """
    logging.info("Launching interactive G-code shell (robot only)...")

    # Create clients and connect
    tcp_client = TcpClientR3(host=ip, port=port)
    tcp_client.connect()

    # Create printer object
    printer = MelfaRobot(tcp_client,
                         number_axes=6,
                         speed_threshold=10,
                         safe_return=safe_return)
    printer.boot()

    # Executing communication
    try:
        while True:
            usr_msg = input("G-Code>")
            if usr_msg.lower() in ["quit"]:
                raise KeyboardInterrupt
            if len(usr_msg) > 0:
                # Parse G-code
                gcode = GCmd.read_cmd_str(usr_msg)
                print(str(gcode))
                printer.assign_task(gcode)
    except MelfaBaseException as e:
        print(str(e))
    except KeyboardInterrupt:
        print('Program terminated by user.')
    finally:
        printer.shutdown()
Exemplo n.º 11
0
def gcode_santa(robot: MelfaRobot):
    gcode = """G1 X-60 Y-60 Z100 F4500
G91
G1 Z-50
G1 X120
G1 Y120
G1 X-120
G1 Y-120
G1 X120 Y120
G1 X-60 Y60
G1 X-60 Y-60
G1 X120 Y-120
G1 X-120 Y0
G02 X0 Y120 J60
G02 X120 I60
G02 Y-120 J-60
G03 X0 Y0 I-60 J60
G1 Z50
G90
G1 X0 Y0"""

    for command in gcode.split("\n"):
        cmd = GCmd.read_cmd_str(command)
        robot.assign_task(cmd)
Exemplo n.º 12
0
def test_linear_segment_from_gcode(cmd, ds, is_absolute, curr_vel, curr_acc,
                                   ex_points, target):
    gcode = GCmd.read_cmd_str(cmd)
    lin_segment = lin_segment_from_gcode(gcode, CURRENT_POSE, ds, is_absolute,
                                         curr_vel, curr_acc)
    common_properties(curr_acc, curr_vel, ds, ex_points, lin_segment, target)
Exemplo n.º 13
0
def check_trajectory(config_f='./../config.ini', gcode_f='./../test.gcode', ip: Optional[str] = None,
                     port: Optional[int] = None):
    """
    Validate a trajectory for a given robot setup.
    :param config_f: File path for the configuration file
    :param gcode_f: File path for the input G-Code file
    :param ip: Optional host address to be used to resolve robot parameters directly
    :param port: Optional port to be used to resolve robot parameters directly
    :return:
    """
    print(f'Reading G-Code from file {gcode_f}.')
    with open(gcode_f, 'r') as f:
        cmd_raw = f.readlines()

    commands = [GCmd.read_cmd_str(cmd_str.strip()) for cmd_str in cmd_raw if not cmd_str.startswith(GCmd.COMMENT)]
    commands = [cmd for cmd in commands if cmd is not None]

    config_parser = ConfigParser()
    config_parser.read(config_f)

    read_param_from_robot = False
    home_position = None
    cartesian_limits = None
    joint_limits = None

    # Parameters that always need to be configured within the config file
    max_jnt_speed = config_parser.get('prechecks', 'max_joint_speed')
    joint_velocity_limits = [float(i) for i in max_jnt_speed.split(', ')]

    # Increments for sampling
    inc_distance_mm = float(config_parser.get('prechecks', 'ds_mm'))
    inc_angle_tool_deg = float(config_parser.get('prechecks', 'dphi_deg', fallback=2 * pi))

    urdf = config_parser.get('prechecks', 'urdf_path')
    default_acc = float(config_parser.get('prechecks', 'default_acc'))

    # Extrusion parameters
    extrusion_height = float(config_parser.get('prechecks', 'extrusion_height'))
    extrusion_width = float(config_parser.get('prechecks', 'extrusion_width'))

    # Tool Center Point offsets
    tool_offset_x = float(config_parser.get('prechecks', 'tool_offset_x', fallback=0))
    tool_offset_y = float(config_parser.get('prechecks', 'tool_offset_y', fallback=0))
    tool_offset_z = float(config_parser.get('prechecks', 'tool_offset_z', fallback=0))

    # Heat bed offsets
    x_hb = float(config_parser.get('prechecks', 'bed_origin_x', fallback=0))
    y_hb = float(config_parser.get('prechecks', 'bed_origin_y', fallback=0))
    z_hb = float(config_parser.get('prechecks', 'bed_origin_z', fallback=0))

    # Learning time
    prm_learning_time_s = int(config_parser.get('prechecks', 'prm_learning_time', fallback=120))

    robot_config = melfa_rv_4a(atoff=tool_offset_z, rtoff=tool_offset_x)

    if ip is not None and port is not None:
        # Parameters can be read from the robot
        try:
            print(f'Attempting to read configuration from {ip}:{port}')
            tcp_client = TcpClientR3(host=ip, port=port)
            protocol = R3Protocol(tcp_client, MelfaCoordinateService())
            home_position = protocol.get_safe_pos().values
            cartesian_limits = protocol.get_xyz_borders()
            joint_limits = protocol.get_joint_borders()
        except ClientError as e:
            print(f'Reading parameters from robot failed due to {e}. Falling back to config file.')
        else:
            read_param_from_robot = True

    if not read_param_from_robot:
        # Parameters that need to be configured in the config file if they are not read from the robot
        home_pos_str = config_parser.get('prechecks', 'home_joints')
        home_position = [float(i) for i in home_pos_str.split(', ')]
        cartesian_limits_str = config_parser.get('prechecks', 'xyz_limits')
        cartesian_limits = [float(i) for i in cartesian_limits_str.split(', ')]
        joint_limits_str = config_parser.get('prechecks', 'joint_limits')
        joint_limits = [float(i) for i in joint_limits_str.split(', ')]

    print('\nConfiguration parameters:')
    print(f'Joint home position in deg: {home_position}')
    home_position = [i / 180 * pi for i in home_position]
    print(f'Cartesian limits in mm: {cartesian_limits}')
    print(f'Joint limits in deg: {joint_limits}')
    joint_limits = [i / 180 * pi for i in joint_limits]
    print(f'Maximum joint velocities in rad/s: {joint_velocity_limits}')
    print(f'Checking resolution in mm: {inc_distance_mm}')
    print(f'URDF filepath: {urdf}')
    print(f'Default acceleration set to {default_acc} mm/s^2')
    print('\n')

    hb_offset = Coordinate([x_hb, y_hb, z_hb], 'XYZ')

    # Create the constraints
    traj_constraint = Constraints(cartesian_limits, joint_limits, joint_velocity_limits)

    # Create the increments
    incs = Increments(inc_distance_mm, inc_angle_tool_deg / 180 * pi)

    # Create the extrusion data
    extr = Extrusion(extrusion_height, extrusion_width)

    try:
        # Check the trajectory
        check_traj(commands, robot_config, traj_constraint, home_position, incs, extr, default_acc, urdf, hb_offset,
                   prm_learning_time_s)
    except (CartesianLimitViolation, WorkspaceViolation):
        logging.error('Please verify that the limits are correct and check the positioning of the part.')
        raise
    except ConfigurationChangesError:
        logging.error('Robot configuration transitions are not supported within a coherent segment.')
        raise
    except JointVelocityViolation:
        logging.error('Autoamtically reducing the speed is not yet supported.')
        raise
    except NoValidPathFound:
        logging.error('Recreating graph (partially) with different tool orientation is unsupported.')
        raise
Exemplo n.º 14
0
 def test_invalid_command_from_str(self, cmd):
     GCmd.CMD_REMOVE_LEAD_ZERO = True
     with pytest.raises(ValueError):
         GCmd.read_cmd_str(cmd)