gcode_nom/binary/gcode_block/
mod.rs1use core::fmt::Display;
2
3use crate::binary::{default_params::param_parser, BlockError};
4use nom::{
5 bytes::streaming::take,
6 combinator::verify,
7 number::streaming::{le_u16, le_u32},
8 sequence::preceded,
9 IResult, Parser,
10};
11
12use super::{
13 block_header::{block_header_parser, BlockHeader},
14 default_params::Param,
15 inflate::decompress_data_block,
16 Markdown,
17};
18
19pub mod extractor;
21pub mod svg;
23
24#[derive(Clone, Debug, Default, PartialEq, Eq)]
28pub struct GCodeBlock<'a> {
29 pub header: BlockHeader,
31 pub param: Param,
33 pub data: &'a [u8],
35 checksum: Option<u32>,
36}
37
38impl Display for GCodeBlock<'_> {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 let datablock: String =
41 match decompress_data_block(self.data, &self.param.encoding, &self.header) {
42 Ok((_remain, data)) => String::from_utf8_lossy(&data).to_string(),
43 Err(_e) => String::from("failed to decompress"),
44 };
45 writeln!(
46 f,
47 "-------------------------- GCodeBlock --------------------------"
48 )?;
49 writeln!(f, "Params")?;
50 writeln!(f, "encoding {:#?}", self.param.encoding)?;
51 writeln!(f)?;
52 writeln!(f, "DataBlock {datablock:?}")?;
53 writeln!(f)?;
54 write!(f, "-------------------------- GCodeBlock ")?;
55 match self.checksum {
56 Some(checksum) => writeln!(f, "Checksum Ox{checksum:X} ---------")?,
57 None => writeln!(f, "No checksum")?,
58 }
59 Ok(())
60 }
61}
62
63impl Markdown for Vec<GCodeBlock<'_>> {
64 fn markdown<W>(&self, f: &mut W) -> core::fmt::Result
65 where
66 W: core::fmt::Write,
67 {
68 if self.is_empty() {
69 return Ok(());
70 }
71 writeln!(f)?;
72 writeln!(f, "## GCodeBlocks")?;
73 for (i, gcode) in self.iter().enumerate() {
74 writeln!(f)?;
75 writeln!(f, "### GCodeBlock {i}")?;
77 writeln!(f)?;
78 gcode.headless_markdown(&mut *f)?;
79 }
80 Ok(())
81 }
82}
83
84impl GCodeBlock<'_> {
85 pub(super) fn headless_markdown<W>(&self, mut f: W) -> core::fmt::Result
87 where
88 W: std::fmt::Write,
89 {
90 let datablock = match decompress_data_block(self.data, &self.param.encoding, &self.header) {
91 Ok((_remain, data)) => String::from_utf8_lossy(&data).to_string(),
92 Err(_e) => String::from("failed to decompress"),
93 };
94 writeln!(f, "### Params")?;
95 writeln!(f)?;
96 writeln!(f, "encoding {:#?}", self.param.encoding)?;
97 writeln!(f)?;
98 writeln!(f, "<details>")?;
99 writeln!(f, "<summary>DataBlock</summary>")?;
100 writeln!(f, "<br>")?;
101 writeln!(f, "{datablock:?}")?;
102 writeln!(f, "</details>")?;
103 writeln!(f)?;
104
105 match self.checksum {
106 Some(checksum) => writeln!(f, "Checksum Ox{checksum:X}")?,
107 None => writeln!(f, "No checksum")?,
108 }
109 Ok(())
110 }
111}
112
113static CODE_BLOCK_ID: u16 = 1u16;
114
115pub fn gcode_parser(input: &[u8]) -> IResult<&[u8], GCodeBlock<'_>, BlockError> {
123 let (after_block_header, header) = preceded(
124 verify(le_u16, |block_type| {
125 log::debug!(
126 "gcode_block: Looking for CODE_BLOCK_ID {CODE_BLOCK_ID} found {block_type} cond {}",
127 *block_type == CODE_BLOCK_ID
128 );
129 *block_type == CODE_BLOCK_ID
130 }),
131 block_header_parser,
132 )
133 .parse(input)
134 .map_err(|e| {
135 log::error!("Failed to parse block header {e}");
136 e.map(|_e| BlockError::Gcode)
137 })?;
138
139 log::info!("Found G-code block id.");
140 let (after_param, param) = param_parser(after_block_header).map_err(|e| {
141 log::error!("Failed to parse param {e}");
142 e.map(|_e| BlockError::Gcode)
143 })?;
144
145 log::info!("param {param:?}");
146 let (after_data, data) = match header.compressed_size {
148 Some(size) => take(size)(after_param)?,
149 None => take(header.uncompressed_size)(after_param)?,
150 };
151
152 let (after_checksum, checksum) = match le_u32::<_, BlockError>(after_data) {
153 Ok((after_checksum, checksum)) => (after_checksum, checksum),
154 Err(_e) => {
155 let msg = "gcode_block: Failed to extract checksum".to_string();
156 log::error!("{msg}");
157 return Err(nom::Err::Error(BlockError::Gcode));
158 }
159 };
160
161 Ok((
162 after_checksum,
163 GCodeBlock {
164 header,
165 param,
166 data,
167 checksum: Some(checksum),
168 },
169 ))
170}
171
172pub fn gcode_parser_with_checksum(input: &[u8]) -> IResult<&[u8], GCodeBlock<'_>, BlockError> {
180 let (remain, gcode) = gcode_parser(input)?;
181 if let Some(checksum) = gcode.checksum {
182 let param_size = 2;
183 let block_size =
184 gcode.header.size_in_bytes() + param_size + gcode.header.payload_size_in_bytes();
185 let crc_input = &input[..block_size];
186 let computed_checksum = crc32fast::hash(crc_input);
187
188 log::debug!("gcode checksum 0x{checksum:04x} computed checksum 0x{computed_checksum:04x} ");
189 if checksum == computed_checksum {
190 log::debug!("checksum match");
191 } else {
192 log::error!("fail checksum");
193 return Err(nom::Err::Error(BlockError::Gcode));
194 }
195 }
196
197 Ok((remain, gcode))
198}