1use crate::error::NanonisError;
2use crate::types::NanonisValue;
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use log::debug;
5use std::io::Read;
6
7pub const COMMAND_SIZE: usize = 32;
9pub const HEADER_SIZE: usize = 40;
10pub const ERROR_INFO_SIZE: usize = 8;
11pub const MAX_RETRY_COUNT: usize = 1000;
12pub const MAX_RESPONSE_SIZE: usize = 100 * 1024 * 1024; pub const RESPONSE_FLAG: u16 = 1;
14pub const ZERO_BUFFER: u16 = 0;
15
16#[derive(Debug, Clone)]
17struct MessageHeader {
18 command: [u8; COMMAND_SIZE],
19 body_size: u32,
20 send_response: u16,
21 _padding: u16,
22}
23
24impl MessageHeader {
25 fn new(command: &str, body_size: u32) -> Self {
26 let mut cmd_bytes = [0u8; COMMAND_SIZE];
27 let cmd_str = command.as_bytes();
28 let len = cmd_str.len().min(COMMAND_SIZE);
29 cmd_bytes[..len].copy_from_slice(&cmd_str[..len]);
30
31 Self {
32 command: cmd_bytes,
33 body_size,
34 send_response: RESPONSE_FLAG, _padding: ZERO_BUFFER,
36 }
37 }
38
39 fn to_bytes(&self) -> [u8; HEADER_SIZE] {
41 let mut buf = [0u8; HEADER_SIZE];
42 buf[0..32].copy_from_slice(&self.command);
43 buf[32..36].copy_from_slice(&self.body_size.to_be_bytes());
44 buf[36..38].copy_from_slice(&self.send_response.to_be_bytes());
45 buf[38..40].copy_from_slice(&self._padding.to_be_bytes());
46 buf
47 }
48}
49
50pub struct Protocol;
52
53impl Protocol {
54 pub fn parse_error_info(
56 body: &[u8],
57 data_end_cursor: usize,
58 ) -> Result<(), NanonisError> {
59 let error_section = match body.get(data_end_cursor..) {
61 Some(section) if section.len() >= ERROR_INFO_SIZE => section,
62 _ => return Ok(()), };
64
65 let (status_bytes, rest) = error_section.split_at(4);
67 let (size_bytes, message_bytes) = rest.split_at(4);
68
69 let error_status =
70 i32::from_be_bytes(status_bytes.try_into().map_err(|_| {
71 NanonisError::Protocol("Invalid error status format".into())
72 })?);
73
74 let error_desc_size =
75 i32::from_be_bytes(size_bytes.try_into().map_err(|_| {
76 NanonisError::Protocol("Invalid error size format".into())
77 })?) as usize;
78
79 if error_desc_size > 0 {
80 let message_slice =
82 message_bytes.get(..error_desc_size).ok_or_else(|| {
83 NanonisError::Protocol("Error message truncated".into())
84 })?;
85
86 let error_msg = std::str::from_utf8(message_slice).map_err(|_| {
88 NanonisError::Protocol("Invalid UTF-8 in error message".into())
89 })?;
90
91 let trimmed_msg = error_msg.trim();
92 if !trimmed_msg.is_empty() {
93 return Err(NanonisError::ServerError {
94 code: error_status,
95 message: trimmed_msg.to_string(),
96 });
97 }
98 }
99
100 Ok(())
101 }
102
103 pub fn read_exact_bytes<const N: usize>(
105 reader: &mut dyn Read,
106 ) -> Result<[u8; N], NanonisError> {
107 debug!("Attempting to read exactly {} bytes", N);
108 let mut buf = [0u8; N];
109
110 match reader.read_exact(&mut buf) {
111 Ok(()) => {
112 debug!(
113 "Successfully read {} bytes: {:02x?}",
114 N,
115 if N <= 20 { &buf[..] } else { &buf[..20] }
116 );
117 Ok(buf)
118 }
119 Err(e) => {
120 debug!("Failed to read {} bytes: {} (kind: {:?})", N, e, e.kind());
121 Err(NanonisError::Io {
122 source: e,
123 context: format!("Failed to read {} bytes from Nanonis", N),
124 })
125 }
126 }
127 }
128
129 pub fn read_variable_bytes(
131 reader: &mut dyn Read,
132 size: usize,
133 ) -> Result<Vec<u8>, NanonisError> {
134 debug!("Attempting to read {} variable bytes", size);
135
136 if size > MAX_RESPONSE_SIZE {
138 debug!("Size {} exceeds maximum {}", size, MAX_RESPONSE_SIZE);
139 return Err(NanonisError::Protocol(format!(
140 "Response size {} exceeds maximum {}",
141 size, MAX_RESPONSE_SIZE
142 )));
143 }
144
145 let mut body = vec![0u8; size];
146 match reader.read_exact(&mut body) {
147 Ok(()) => {
148 debug!(
149 "Successfully read {} variable bytes: {:02x?}",
150 size,
151 if size <= 50 { &body[..] } else { &body[..50] }
152 );
153 Ok(body)
154 }
155 Err(e) => {
156 debug!(
157 "Failed to read {} variable bytes: {} (kind: {:?})",
158 size,
159 e,
160 e.kind()
161 );
162 let mut partial_buf = Vec::new();
164 if let Ok(bytes_read) = reader.read_to_end(&mut partial_buf) {
165 debug!(
166 "Partial read got {} bytes: {:02x?}",
167 bytes_read,
168 if bytes_read <= 50 {
169 &partial_buf[..]
170 } else {
171 &partial_buf[..50]
172 }
173 );
174 }
175 Err(NanonisError::Io {
176 source: e,
177 context: format!("Failed to read {} byte response body", size),
178 })
179 }
180 }
181 }
182
183 pub fn parse_response_with_error_check(
185 response: &[u8],
186 response_types: &[&str],
187 ) -> Result<Vec<NanonisValue>, NanonisError> {
188 let values = Self::parse_response(response, response_types)?;
190
191 let cursor = Self::calculate_cursor_position(response, response_types)?;
193
194 Self::parse_error_info(response, cursor)?;
196
197 Ok(values)
198 }
199
200 fn calculate_cursor_position(
202 response: &[u8],
203 response_types: &[&str],
204 ) -> Result<usize, NanonisError> {
205 let mut cursor = std::io::Cursor::new(response);
206 let mut result = Vec::with_capacity(response_types.len());
207
208 for &response_type in response_types {
211 match response_type {
212 "H" => {
213 cursor.read_u16::<BigEndian>()?;
214 }
215 "h" => {
216 cursor.read_i16::<BigEndian>()?;
217 }
218 "I" => {
219 let val = cursor.read_u32::<BigEndian>()?;
220 result.push(val);
221 }
222 "i" => {
223 let val = cursor.read_i32::<BigEndian>()? as u32;
224 result.push(val);
225 }
226 "f" => {
227 cursor.read_f32::<BigEndian>()?;
228 }
229 "d" => {
230 cursor.read_f64::<BigEndian>()?;
231 }
232
233 t if t.contains("*f") => {
234 let len = if t.starts_with("+") {
235 cursor.read_u32::<BigEndian>()? as usize
236 } else if let Some(&prev_val) = result.last() {
237 prev_val as usize
238 } else {
239 return Err(NanonisError::Protocol(
240 "Array length not specified".to_string(),
241 ));
242 };
243
244 for _ in 0..len {
245 cursor.read_f32::<BigEndian>()?;
246 }
247 }
248
249 t if t.contains("*d") => {
250 let len = if t.starts_with("+") {
251 cursor.read_u32::<BigEndian>()? as usize
252 } else if let Some(&prev_val) = result.last() {
253 prev_val as usize
254 } else {
255 return Err(NanonisError::Protocol(
256 "Array length not specified".to_string(),
257 ));
258 };
259
260 for _ in 0..len {
261 cursor.read_f64::<BigEndian>()?;
262 }
263 }
264
265 t if t.contains("*i") => {
266 let len = if t.starts_with("+") {
267 cursor.read_u32::<BigEndian>()? as usize
268 } else if let Some(&prev_val) = result.last() {
269 prev_val as usize
270 } else {
271 return Err(NanonisError::Protocol(
272 "Array length not specified".to_string(),
273 ));
274 };
275
276 for _ in 0..len {
277 cursor.read_i32::<BigEndian>()?;
278 }
279 }
280
281 "+*c" => {
282 let _total_size = cursor.read_u32::<BigEndian>()?;
283 let num_strings = cursor.read_u32::<BigEndian>()? as usize;
284
285 for _ in 0..num_strings {
286 let string_len = cursor.read_u32::<BigEndian>()? as usize;
287 let mut string_bytes = vec![0u8; string_len];
288 cursor.read_exact(&mut string_bytes)?;
289 }
290 }
291
292 "*-c" => {
293 let string_length = result.last().ok_or_else(|| {
294 NanonisError::Protocol(
295 "String length not found for *-c type".to_string(),
296 )
297 })?;
298
299 let mut string_bytes = vec![0u8; *string_length as usize];
300 cursor.read_exact(&mut string_bytes)?;
301 }
302
303 "2f" => {
304 if result.len() < 2 {
305 return Err(NanonisError::Protocol(
306 "2D array dimensions not found".to_string(),
307 ));
308 }
309
310 let rows = result[result.len() - 2] as usize;
311 let cols = result[result.len() - 1] as usize;
312
313 for _ in 0..(rows * cols) {
314 cursor.read_f32::<BigEndian>()?;
315 }
316 }
317
318 _ => {
319 return Err(NanonisError::Type(format!(
320 "Unsupported response type: {response_type}"
321 )));
322 }
323 };
324 }
325
326 Ok(cursor.position() as usize)
327 }
328
329 pub fn serialize_value(
331 value: &NanonisValue,
332 body_type: &str,
333 buffer: &mut Vec<u8>,
334 ) -> Result<(), NanonisError> {
335 match (value, body_type) {
336 (NanonisValue::U16(v), "H") => buffer.write_u16::<BigEndian>(*v)?,
337 (NanonisValue::I16(v), "h") => buffer.write_i16::<BigEndian>(*v)?,
338 (NanonisValue::U32(v), "I") => buffer.write_u32::<BigEndian>(*v)?,
339 (NanonisValue::I32(v), "i") => buffer.write_i32::<BigEndian>(*v)?,
340 (NanonisValue::F32(v), "f") => buffer.write_f32::<BigEndian>(*v)?,
341 (NanonisValue::F64(v), "d") => buffer.write_f64::<BigEndian>(*v)?,
342
343 (NanonisValue::String(s), t) if t.contains("*c") => {
344 let bytes = s.as_bytes();
345 if t.starts_with("+") {
346 buffer.write_u32::<BigEndian>(bytes.len() as u32)?;
347 }
348 buffer.extend_from_slice(bytes);
349 }
350
351 (NanonisValue::ArrayI32(arr), t) if t.contains("*i") => {
352 if t.starts_with("+") {
353 buffer.write_u32::<BigEndian>(arr.len() as u32)?;
354 }
355 for &val in arr {
356 buffer.write_i32::<BigEndian>(val)?;
357 }
358 }
359
360 (NanonisValue::ArrayU32(arr), t) if t.contains("*I") => {
361 if t.starts_with("+") {
362 buffer.write_u32::<BigEndian>(arr.len() as u32)?;
363 }
364 for &val in arr {
365 buffer.write_u32::<BigEndian>(val)?;
366 }
367 }
368
369 (NanonisValue::ArrayF32(arr), t) if t.contains("*f") => {
370 if t.starts_with("+") {
371 buffer.write_u32::<BigEndian>(arr.len() as u32)?;
372 }
373 for &val in arr {
374 buffer.write_f32::<BigEndian>(val)?;
375 }
376 }
377
378 (NanonisValue::ArrayF64(arr), t) if t.contains("*d") => {
379 if t.starts_with("+") {
380 buffer.write_u32::<BigEndian>(arr.len() as u32)?;
381 }
382 for &val in arr {
383 buffer.write_f64::<BigEndian>(val)?;
384 }
385 }
386
387 _ => {
388 return Err(NanonisError::Type(format!(
389 "Unsupported type combination: {value:?} with {body_type}"
390 )))
391 }
392 }
393 Ok(())
394 }
395
396 pub fn parse_response(
398 response: &[u8],
399 response_types: &[&str],
400 ) -> Result<Vec<NanonisValue>, NanonisError> {
401 let mut cursor = std::io::Cursor::new(response);
402 let mut result = Vec::with_capacity(response_types.len());
403
404 for &response_type in response_types {
405 let value = match response_type {
406 "H" => NanonisValue::U16(cursor.read_u16::<BigEndian>()?),
407 "h" => NanonisValue::I16(cursor.read_i16::<BigEndian>()?),
408 "I" => NanonisValue::U32(cursor.read_u32::<BigEndian>()?),
409 "i" => NanonisValue::I32(cursor.read_i32::<BigEndian>()?),
410 "f" => NanonisValue::F32(cursor.read_f32::<BigEndian>()?),
411 "d" => NanonisValue::F64(cursor.read_f64::<BigEndian>()?),
412
413 t if t.contains("*f") => {
414 let len = if t.starts_with("+") {
415 cursor.read_u32::<BigEndian>()? as usize
416 } else if let Some(prev_val) = result.last() {
417 match prev_val {
418 NanonisValue::U32(len) => *len as usize,
419 NanonisValue::I32(len) => *len as usize,
420 _ => {
421 return Err(NanonisError::Protocol(
422 "Array length not found".to_string(),
423 ))
424 }
425 }
426 } else {
427 return Err(NanonisError::Protocol(
428 "Array length not specified".to_string(),
429 ));
430 };
431
432 let mut arr = Vec::with_capacity(len);
433 for _ in 0..len {
434 arr.push(cursor.read_f32::<BigEndian>()?);
435 }
436 NanonisValue::ArrayF32(arr)
437 }
438
439 t if t.contains("*d") => {
440 let len = if t.starts_with("+") {
441 cursor.read_u32::<BigEndian>()? as usize
442 } else if let Some(prev_val) = result.last() {
443 match prev_val {
444 NanonisValue::U32(len) => *len as usize,
445 NanonisValue::I32(len) => *len as usize,
446 _ => {
447 return Err(NanonisError::Protocol(
448 "Array length not found".to_string(),
449 ))
450 }
451 }
452 } else {
453 return Err(NanonisError::Protocol(
454 "Array length not specified".to_string(),
455 ));
456 };
457
458 let mut arr = Vec::with_capacity(len);
459 for _ in 0..len {
460 arr.push(cursor.read_f64::<BigEndian>()?);
461 }
462 NanonisValue::ArrayF64(arr)
463 }
464
465 t if t.contains("*i") => {
466 let len = if t.starts_with("+") {
467 cursor.read_u32::<BigEndian>()? as usize
468 } else if let Some(prev_val) = result.last() {
469 match prev_val {
470 NanonisValue::U32(len) => *len as usize,
471 NanonisValue::I32(len) => *len as usize,
472 _ => {
473 return Err(NanonisError::Protocol(
474 "Array length not found".to_string(),
475 ))
476 }
477 }
478 } else {
479 return Err(NanonisError::Protocol(
480 "Array length not specified".to_string(),
481 ));
482 };
483
484 let mut arr = Vec::with_capacity(len);
485 for _ in 0..len {
486 arr.push(cursor.read_i32::<BigEndian>()?);
487 }
488 NanonisValue::ArrayI32(arr)
489 }
490
491 "+*c" => {
493 let _total_size = cursor.read_u32::<BigEndian>()?;
495 let num_strings = cursor.read_u32::<BigEndian>()? as usize;
497 let mut strings = Vec::with_capacity(num_strings);
498
499 for _ in 0..num_strings {
500 let string_len = cursor.read_u32::<BigEndian>()? as usize;
501 let mut string_bytes = vec![0u8; string_len];
502 cursor.read_exact(&mut string_bytes)?;
503 let string =
504 String::from_utf8_lossy(&string_bytes).to_string();
505 strings.push(string);
506 }
507
508 NanonisValue::ArrayString(strings)
509 }
510
511 "*-c" => {
513 let string_length = match result.last() {
515 Some(NanonisValue::I32(len)) => *len as usize,
516 Some(NanonisValue::U32(len)) => *len as usize,
517 _ => {
518 return Err(NanonisError::Protocol(
519 "String length not found for *-c type".to_string(),
520 ))
521 }
522 };
523
524 let mut string_bytes = vec![0u8; string_length];
526 cursor.read_exact(&mut string_bytes)?;
527 let string = String::from_utf8_lossy(&string_bytes).to_string();
528
529 NanonisValue::String(string)
530 }
531
532 "2f" => {
533 if result.len() < 2 {
535 return Err(NanonisError::Protocol(
536 "2D array dimensions not found".to_string(),
537 ));
538 }
539
540 let rows = match result[result.len() - 2] {
541 NanonisValue::I32(r) => r as usize,
542 _ => {
543 return Err(NanonisError::Protocol(
544 "Invalid row count for 2D array".to_string(),
545 ))
546 }
547 };
548
549 let cols = match result[result.len() - 1] {
550 NanonisValue::I32(c) => c as usize,
551 _ => {
552 return Err(NanonisError::Protocol(
553 "Invalid column count for 2D array".to_string(),
554 ))
555 }
556 };
557
558 let mut data_2d = Vec::with_capacity(rows);
560
561 for _ in 0..rows {
562 let mut row_data = Vec::with_capacity(cols);
563 for _ in 0..cols {
564 row_data.push(cursor.read_f32::<BigEndian>()?);
565 }
566 data_2d.push(row_data);
567 }
568
569 NanonisValue::Array2DF32(data_2d)
570 }
571
572 _ => {
573 return Err(NanonisError::Type(format!(
574 "Unsupported response type: {response_type}"
575 )))
576 }
577 };
578
579 result.push(value);
580 }
581
582 Ok(result)
583 }
584
585 pub fn create_command_header(command: &str, body_size: u32) -> Vec<u8> {
587 let header = MessageHeader::new(command, body_size);
588 header.to_bytes().to_vec()
589 }
590
591 pub fn validate_response_header(
593 header: &[u8; HEADER_SIZE],
594 expected_command: &str,
595 ) -> Result<u32, NanonisError> {
596 let response_body_size =
598 u32::from_be_bytes([header[32], header[33], header[34], header[35]]);
599
600 let received_command = String::from_utf8_lossy(&header[0..COMMAND_SIZE])
602 .trim_end_matches('\0')
603 .trim_end_matches('0')
604 .to_string();
605
606 if received_command == expected_command {
607 Ok(response_body_size)
608 } else {
609 Err(NanonisError::CommandMismatch {
610 expected: expected_command.to_string(),
611 actual: received_command,
612 })
613 }
614 }
615}