gcode-rs icon indicating copy to clipboard operation
gcode-rs copied to clipboard

Testing on hardware and in real applications

Open Michael-F-Bryan opened this issue 7 years ago • 9 comments

I'm at a point where this crate will need a bit of testing in the real world to work out what the most ergonomic way of doing things is.

I'm looking for people who are experimenting with embedded devices and need to work with gcode. If you are developing a desktop application that deals with gcodes, I'm keen for your feedback too!

If you try out this crate and have any feedback, or if you are just experimenting and want to know how best to use it, let me know by making a comment here and I'll try to do everything I can to help out :)

Michael-F-Bryan avatar May 25 '17 16:05 Michael-F-Bryan

You don't have an example of the high level parser yet, looking at the test code it looks like you are passing the results of the low level parser through println! to the high level parser:

let lines = low_level_parser .inspect(|c| println!("{:?}", c)) .collect::<Result<Vec<_>, >>() .unwrap(); let strongly_typed: Vec<> = lines.into_iter().map(type_check).collect();

since println! is part of std this won't work for embedded. Is there a different way to use the high level parser?

etrombly avatar Jun 06 '17 18:06 etrombly

The inspect() method does is lets you inspect the contents of an iterator without consuming or changing it, and I was using that to see what each element in the iterator was.

You should be able to delete it without affecting anything.

Michael-F-Bryan avatar Jun 07 '17 01:06 Michael-F-Bryan

Great, got a quick version working this way:

let lexer = Tokenizer::new(GCODE.chars());
        let tokens = lexer.filter_map(|t| t.ok());
        let parser = BasicParser::new(tokens);
        for line in parser {
            if let Line::Cmd(x) = line.unwrap() {
                if x.command() == (CommandType::G, 0) {
                    let args = x.args();
                    for arg in args.iter(){
                        match arg.kind {
                            ArgumentKind::X => new_x = arg.value,
                            ArgumentKind::Y => new_y = arg.value,
                            _ => {},
                        }
                    }
                }

here's the full code (will make a repo once it's cleaned up a little more). https://pastebin.com/fZTJsquH

etrombly avatar Jun 07 '17 18:06 etrombly

@etrombly wow, that looks awesome! Keep me updated on how you're going and if there's anything I can do to make the gcode library easier to use. It looks like it was really cumbersome to try and get the whole lexer/parser thing set up :(

Michael-F-Bryan avatar Jun 07 '17 19:06 Michael-F-Bryan

It was actually pretty easy to use once I figured it out. Just had to dig around your tests to find examples. I think having an example like what I wrote would help people getting started with it. If I were making it more generic I would match on the command type and command number though.

etrombly avatar Jun 07 '17 19:06 etrombly

I just rewrote the parser and deprecated the old low_level one. The usage should be pretty much the same, I've just rearranged how you access things.

extern crate gcode;

use gcode::{Parser, Tokenizer, Line};
use gcode::parser::Number;

fn main() {
    let src = include_str!("../tests/data/PI_octcat.gcode");

    let lexer = Tokenizer::new(src.chars());
    let tokens = lexer.filter_map(|t| t.ok());

    let parser = Parser::new(tokens);

    for line in parser {
        handle_line(line.unwrap());
    }
}

fn handle_line(line: Line) {
    match line {
        Line::ProgramNumber(n) => {}
        Line::Cmd(cmd) => {
            match (cmd.kind, cmd.number) {
                (CommandKind::G, Number::Integer(90)) => { ... }
                _ => {}    
            }
        }
    }
}

It's a little annoying because of the rightward drift and because you need to explicitly deal with the fact that the number for a G or M code may be a decimal. So The number is actually an enum which can either be an Integer(u32), or a Decimal(u32, u32) (the numbers before and after the decimal point).

Michael-F-Bryan avatar Jun 09 '17 01:06 Michael-F-Bryan

Just started working on this again since japaric updated RTFM. One way to handle rightward drift is:

const G0: (CommandKind, Number) = (CommandKind::G, Number::Integer(0));
match (cmd.kind, cmd.number) => {
    G0 => { ... }
    - => {}
}

A bit of a hassle defining all the constants though. Not sure how much additional size it would add to the library either.

etrombly avatar Feb 16 '18 23:02 etrombly

Hello, I am trying to write a small command line tool for preprocessing Gcode using this library. I first went with trying to apply a translation to every X position using the following prototype:

extern crate gcode;

use gcode::*;

fn main() {
    let src = "O1000
    T1 M6
    G90
    G01 X-75 Y-75 S500 M3
    G43 Z100 H1
    G01 Z5
    N20 G01 Z-20 F100";

    let translate_x = 42.0;

    let mut lines = gcode::parse(src);
    let mut new_lines = vec!();
    for line in lines {
        if let Some(x) = line.value_for('X') {
            let mut new_line = Gcode::new(line.mnemonic(), line.number(), line.span());
            for word in line.args() {
                if word.letter == 'X' {
                    let new_arg = Word::new(word.letter, word.value + translate_x, word.span);
                    new_line.add_argument(new_arg);
                } else {
                    new_line.add_argument(word.clone());
                }
            }
            new_lines.push(new_line);
        } else {
            new_lines.push(line);
        }

    }

    for line in new_lines {
        println!("{}", line);
    }
}

This works well and translates the X value just fine, but I found it cumbersome to create a new Gcode and Word from existing ones. Maybe I am doing something wrong here, any idea on how to make this look better?

gagath avatar Sep 19 '18 12:09 gagath

I'd never thought of updating a gcode in place, would it be easier if we had an args_mut() method which yields items of &mut Word?

What are your thoughts on this?

use gcode::{self, Gcode, Word};

fn translate_x(mut code: Gcode, dx: f32) -> Gcode {
  for arg in code.args_mut() {
    if arg.letter == 'X' {
      arg.value += dx;
    }
  }

  code
}

fn main() {
  let src = "O1000
    T1 M6
    G90
    G01 X-75 Y-75 S500 M3
    G43 Z100 H1
    G01 Z5
    N20 G01 Z-20 F100";

  let dx = 42.0;

  let translated = gcode::parse(src).map(|g| translate_x(g, dx));

  for code in translated {
    println!("{}", code);
  }
}

EDIT: And now it's a thing :stuck_out_tongue_winking_eye:

Michael-F-Bryan avatar Sep 19 '18 13:09 Michael-F-Bryan