You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
4.6 KiB

use std::io::{self, Write};
use std::ops::AddAssign;
#[derive(Clone, PartialEq, Eq)]
pub enum Direction {
Clockwise,
AntiClockwise,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Tool {
Off,
On,
}
#[derive(Clone, PartialEq)]
pub enum Distance {
Absolute,
Incremental,
}
#[derive(Default, PartialEq, Clone)]
pub struct Program(Vec<GCode>);
impl std::ops::Deref for Program {
type Target = [GCode];
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl AddAssign for Program {
fn add_assign(&mut self, mut other: Program) {
self.0.extend(other.0.drain(..));
}
}
impl From<Vec<GCode>> for Program {
fn from(v: Vec<GCode>) -> Self {
Self(v)
}
}
impl Program {
pub fn push(&mut self, g: GCode) {
self.0.push(g)
}
}
macro_rules! write_if_some {
($w:expr, $s:expr, $v:ident) => {
if let Some(v) = $v {
write!($w, $s, v)
} else {
Ok(())
}
};
}
/// Rudimentary regular expression GCode validator.
pub fn validate_gcode(gcode: &&str) -> bool {
use regex::Regex;
let re = Regex::new(r##"^(?:(?:%|\(.*\)|(?:[A-Z^E^U][+-]?\d+(?:\.\d*)?))\h*)*$"##).unwrap();
gcode.lines().all(|line| re.is_match(line))
}
#[derive(Clone, PartialEq)]
pub enum GCode {
RapidPositioning {
x: Option<f64>,
y: Option<f64>,
},
LinearInterpolation {
x: Option<f64>,
y: Option<f64>,
z: Option<f64>,
f: Option<f64>,
},
Dwell {
p: f64,
},
UnitsInches,
UnitsMillimeters,
ProgramEnd,
StartSpindle {
d: Direction,
s: f64,
},
StopSpindle,
DistanceMode(Distance),
Comment(Box<String>),
Raw(Box<String>),
}
pub fn program2gcode<W: Write>(p: &Program, mut w: W) -> io::Result<()> {
use GCode::*;
let mut last_feedrate: Option<f64> = None;
for code in p.iter() {
match code {
RapidPositioning { x, y } => {
if let (None, None) = (x, y) {
continue;
}
write!(w, "G0")?;
write_if_some!(w, " X{}", x)?;
write_if_some!(w, " Y{}", y)?;
writeln!(w)?;
}
LinearInterpolation { x, y, z, f } => {
if let (None, None, None, None) = (x, y, z, f) {
continue;
}
let f = match (last_feedrate, f) {
(None, None) => {
return Err(io::Error::new(
io::ErrorKind::Other,
"Linear interpolation without previously set feedrate",
))
}
(Some(last), Some(new)) => {
if (last - *new).abs() < std::f64::EPSILON {
last_feedrate = Some(*new);
Some(new)
} else {
None
}
}
(None, Some(new)) => {
last_feedrate = Some(*new);
Some(new)
}
(Some(_), None) => None,
};
write!(w, "G1")?;
write_if_some!(w, " X{}", x)?;
write_if_some!(w, " Y{}", y)?;
write_if_some!(w, " Z{}", z)?;
write_if_some!(w, " F{}", f)?;
writeln!(w)?;
}
Dwell { p } => {
writeln!(w, "G4 P{}", p)?;
}
UnitsInches => {
writeln!(w, "G20")?;
}
UnitsMillimeters => {
writeln!(w, "G21")?;
}
ProgramEnd => {
writeln!(w, "M20")?;
}
StartSpindle { d, s } => {
let d = match d {
Direction::Clockwise => 3,
Direction::AntiClockwise => 4,
};
writeln!(w, "M{} S{}", d, s)?;
}
StopSpindle => {
writeln!(w, "M5")?;
}
DistanceMode(mode) => {
writeln!(
w,
"G{}",
match mode {
Distance::Absolute => 90,
Distance::Incremental => 91,
}
)?;
}
Comment(name) => {
writeln!(w, "({})", name)?;
}
Raw(raw) => {
writeln!(w, "{}", raw)?;
}
}
}
Ok(())
}