Skip to content

Files

Latest commit

 

History

History
 
 

g-code-testing

G-Code Parser

Framework supporting the following:

  • Running protocols against emulation
  • Capturing called G-Codes of emulated protocols
  • Parsing captured G-Codes into human-readable text or JSON format
  • Diffing human-readable text or JSON format
  • CLI access to all the above features

Test Cases

The following section is to explain why each test case inside the g-code-testing project has either been included or omitted. Also, it will also explain at a high-level, the different categories of tests.

Test Categories

Protocol Tests

Description: These tests have a Python Protocol file as an input. The framework will run the protocol and collect all G-Code output.

Tests

  • protocol_2_modules - Test using 2 modules in the same protocol
  • protocol_2_single_channel - Test using 2 single channel pipettes in the same protocol
  • protocol_smoothie - Test an extremely simple protocol with no modules
  • protocol_swift_smoke - Test the "smoke" version of the Swift Turbo protocol
  • protocol_swift_turbo - Test the actual Swift Turbo protocol

HTTP Tests

Description: The tests execute the underlying function from calling an HTTP endpoint and collect all the G-Code output. Some HTTP endpoints have been skipped.

Control Tests:

These tests cover the movement of the gantry and pipettes

  • Implemented Tests
    • http_move_left_pipette - Test moving the left pipette with the robot/move HTTP endpoint
    • http_move_right_pipette - Test moving the right pipette with the robot/move HTTP endpoint
    • http_move_left_mount - Test moving the left mount with the robot/move HTTP endpoint
    • http_move_right_mount - Test moving the right mount with the robot/move HTTP endpoint
    • http_home_robot - Test homing the gantry with the robot/home HTTP endpoint
    • http_home_left_pipette - Test homing the left pipette with the robot/home HTTP endpoint
    • http_home_right_pipette - Test homing the right pipette with the robot/home HTTP endpoint
  • Skipped Tests
    • identify endpoint - Only goes to the Raspberry Pi, does not generate any G-Code
    • robot/positions endpoint - Tests by issuing move commands which is already covered
    • robot/lights endpoint - Only goes to the Raspberry Pi, does not generate any G-Code

Module Tests: <-- Insert link here

These tests cover the functionality of all modules

  • Magdeck
    • Implemented Tests
      • http_magdeck_calibrate - Test the calibrate command with the /modules/{serial} HTTP endpoint. This command performs the automatic calibration of the magdeck.
      • http_magdeck_engage - Test engage command with the /modules/{serial} HTTP endpoint. This command lifts the magnets.
      • http_magdeck_deactivate - Test deactivate command with the /modules/{serial} HTTP endpoint. This command returns the magnets to home.
    • Skipped Tests
      • Getters for all the properties on the magdeck because they do not generate any G-Code
      • bootloader method - Because it is way too complicated to try to run this
      • /modules/{serial}/update endpoint - Have to inject a bundled_fw arg into the function and the return isn't worth it
  • Tempdeck
    • Implemented Tests
      • http_tempdeck_start_set_temp - Test start_set_temperature command with the /modules/{serial} HTTP endpoint. This command sets the temperature and exits. It does not wait for the temp deck to come to temperature.
      • http_tempdeck_set_temp - Test set_temperature command with the /modules/{serial} HTTP endpoint. This command sets the temperature and waits for the tempdeck to come up to temperature.
      • http_tempdeck_deactivate - Test deactivate command with the /modules/{serial} HTTP endpoint. This command returns stops any heating or cooling and turns off the fan
    • Skipped Tests
      • Getters for all the properties on the tempdeck because they do not generate any G-Code
      • bootloader method - Because it is way too complicated to try to run this
      • /modules/{serial}/update endpoint - Have to inject a bundled_fw arg into the function and the return isn't worth it
      • wait_next_poll method - It is covered by http_tempdeck_set_temp
      • await_temperature method - It does not return any unique G-Code
  • Thermocycler
    • Implemented Tests
      • http_thermocycler_deactivate_lid - Test deactivate_lid command with the /modules/{serial} HTTP endpoint. This command turns off the heating pad on the thermocycler lid.
      • http_thermocycler_deactivate_block - Test deactivate_block command with the /modules/{serial} HTTP endpoint. This command turns off the heating pad on the thermocycler block.
      • http_thermocycler_deactivate - Test deactivate command with the /modules/{serial} HTTP endpoint. This command turns off the heating pad on the thermocycler block and lid.
      • http_thermocycler_open - Test open command with the /modules/{serial} HTTP endpoint. This command opens the thermocycler lid.
      • http_thermocycler_close - Test close command with the /modules/{serial} HTTP endpoint. This command closes the thermocycler lid.
      • http_thermocycler_set_temp - Test set_temperature command with the /modules/{serial} HTTP endpoint. This command sets the temperature of the thermocycler.
      • http_thermocycler_cycle_temps - Test cycle_temperatures command with the /modules/{serial} HTTP endpoint. This command cycles through multiple temperatures on the thermocycler.
      • http_thermocycler_set_lid_temp - Test set_lid_temperature command with the /modules/{serial} HTTP endpoint. This command sets the lid temperature of the thermocycler.
    • Skipped Tests
      • Getters for all the properties on the thermocycler because they do not generate any G-Code
      • bootloader method - Because it is way too complicated to try to run this
      • hold_time_probably_set method - Because it doesn't generate any G-Code

Setup

  1. Navigate into g-code-testing directory
  2. Run make setup

Python Framework

G-Code Parser

Using the GCodeEngine you can run a Python protocol file against emulation. The GCodeEngine instance will return a GCodeProgram instance

Example:

"""
Using default settings, run protocol at /my/absolute/path/to/my/protocol.py
against emulation.
In "Concise" format store parsed G-Code to /where/I/want/to/store/my/output.txt
"""
import os.path
from g_code_parsing.g_code_engine import HTTPGCodeEngine
from g_code_parsing.g_code_program.supported_text_modes import SupportedTextModes
from opentrons.hardware_control.emulation.settings import Settings

PROTOCOL_PATH = os.path.join('my', 'absolute', 'path', 'to', 'my', 'protocol.py')
OUTPUT_PATH = os.path.join('where', 'I', 'want', 'to', 'store', 'my', 'output.txt')

settings = Settings()  # Using default settings defined in class
with HTTPGCodeEngine(settings).run_protocol(PROTOCOL_PATH) as program:
    program.save_text_explanation_to_file(OUTPUT_PATH, SupportedTextModes.CONCISE)

G-Code Differ

Using the GCodeDiffer you can compare 2 files and return the differences between them in HTML format.

Example:

"""
Compare 2 files and save the diff to a file
"""
import os.path
from g_code_parsing.g_code_differ import GCodeDiffer
FILE_1_PATH = os.path.join('tmp', 'file_1.txt')
FILE_2_PATH = os.path.join('tmp', 'file_2.txt')
HTML_PATH = os.path.join('home', 'derek_maggio', 'Desktop', 'my_diff.html')

with open(FILE_1_PATH, 'r') as file_1, open(FILE_2_PATH, 'r') as file_2:
    file_1_content = '\n'.join(file_1.readlines())
    file_2_content = '\n'.join(file_2.readlines())
    GCodeDiffer(file_1_content, file_2_content).save_html_diff_to_file(HTML_PATH)

CLI

Using the CLI you can access the above functionality through the command line.

Supported commands:

  • run - Execute passed file
  • diff - Diff passed files
  • configurations - Print available files to run

Viewing Help Message

To view available commands run:

pipenv run python -m g_code_parsing.cli -h

To view help for specific command run:

pipenv run python -m g_code_parsing.cli <your_command> -h

For example,

pipenv run python -m g_code_parsing.cli run -h

Running Protocol

To run a protocol with the CLI use the run command. It has the format of

cli.py run [-h] [--text-mode Concise | Default | G-Code]
                  [--left-pipette left_pipette_config]
                  [--right-pipette right_pipette_config]
                  protocol_file_path

Usage Example:

pipenv run python -m g_code_parsing.cli run \
  --text-mode G-Code \
  --left-pipette '{"model": "p20_single_v2.0", "id": "P20SV202020070101"}' \
  protocols/smoothie_protocol.py

Command Breakdown

--text-mode

The --text-mode option determines the output format of running the protocol.

It has 3 choices for text modes: Default, Concise, and G-Code.

  • Default - The most verbose format. Provides raw G-Code, an explanation of what the G-Code does, and an explanation of the response.
  • Concise - Contains all the same information as Default but condenses it to a single line. Instead of headers it uses -> to delimit the G-Code, explanation, and response.
  • G-Code - Contains only the raw G-Code and the raw response. Delimited by ->.

Default Output Example:

Code: M114.2
Explanation: Getting current position for all axes
Response: The current position of the robot is:
        A Axis: 105.29
        B Axis: -8.5
        C Axis: 19.0
        X Axis: 113.38
        Y Axis: 11.24
        Z Axis: 218.0
-----------------------------------------
Code: M400
Explanation: Waiting for motors to stop moving
Response:
-----------------------------------------
Code: M907 A0.1 B0.05 C1.0 X0.3 Y0.3 Z0.1
Explanation: Setting the current (in amps) to:
        X-Axis Motor: 0.3
        Y-Axis Motor: 0.3
        Z-Axis Motor: 0.1
        A-Axis Motor: 0.1
        B-Axis Motor: 0.05
        C-Axis Motor: 1.0
Response:
-----------------------------------------
Code: G4 P0.005
Explanation: Pausing movement for 0.005ms
Response:

Concise Output Example:

M114.2 -> Getting current position for all axes -> The current position of the robot is: A Axis: 105.29 B Axis: -8.5 C Axis: 19.0 X Axis: 113.38 Y Axis: 11.24 Z Axis: 218.0
M400 -> Waiting for motors to stop moving ->
M907 A0.1 B0.05 C1.0 X0.3 Y0.3 Z0.1 -> Setting the current (in amps) to: X-Axis Motor: 0.3 Y-Axis Motor: 0.3 Z-Axis Motor: 0.1 A-Axis Motor: 0.1 B-Axis Motor: 0.05 C-Axis Motor: 1.0 ->
G4 P0.005 -> Pausing movement for 0.005ms ->

G-Code Output Example:

M114.2 -> M114.2 MCS: A:105.29 B:-8.5 C:19.0 X:113.38 Y:11.24 Z:218.0
M400 ->
M907 A0.1 B0.05 C1.0 X0.3 Y0.3 Z0.1 ->
G4 P0.005 ->

Option Usage Example:

--text-mode Default

--left-pipette --right-pipette

The --left-pipette and --right-pipette options are the way to provide the configuration of the pipettes for the OT-2 Emulator. The expected format is JSON.

The JSON expects 2 keys id and model.

Option Usage Example:

--left-pipette '{"model": "p20_single_v2.0", "id": "P20SV202020070101"}'

protocol_file_path

The protocol file path can either be an absolute or relative file path to the Python protocol file that you want to run.

Argument Usage Example:

/absolute/path/to/my/file.py

OR

../relative/path/to/my/file.py

Diffing Output Files

To diff 2 output files with the CLI use the diff command.

The command will return an HTML encoded diff.

It has the format of

usage: cli.py diff [-h] file_path_1 file_path_2

Usage Example:

pipenv run python -m g_code_parsing.cli diff /file/path/one.txt /file/path/two.txt

Command Breakdown

file_path_1 file_path_2

The paths to the files you want to diff

Argument Usage Example:

/absolute/path/to/my/file.py

OR

../relative/path/to/my/file.py

Updating S3

When we make a change to the code that affects the Master G-Code File we have to update it in S3. To do this follow these steps.

  1. Switch to the branch that is failing and pull the changes
  2. Run make teardown in the root of the repo
  3. Run make setup in the root of the repo
  4. Go into g-code-testing and run the test that is failing
    1. For example: pipenv run python cli.py diff protocol/2_modules
    2. Once you have confirmed that it is failing, push to s3
  5. For example: pipenv run python cli.py push protocol/2_modules
  6. Re-run tests locally and in CI