Nicola Belluti
b2b5f0c66c
Some checks failed
Check, format and test / build (push) Failing after 49s
153 lines
5.4 KiB
Rust
153 lines
5.4 KiB
Rust
use crate::RecordIterator;
|
|
use std::io::{BufRead, BufReader, Error, ErrorKind::InvalidData, Result, Write};
|
|
use std::net::{TcpStream, ToSocketAddrs};
|
|
|
|
#[derive(Debug)]
|
|
pub struct R701 {
|
|
tcp_stream: TcpStream,
|
|
sequence_number: u16,
|
|
}
|
|
|
|
impl R701 {
|
|
pub fn connect(connection_info: impl ToSocketAddrs) -> Result<Self> {
|
|
// Create a new R701 struct
|
|
let mut new = Self {
|
|
tcp_stream: TcpStream::connect(connection_info)?,
|
|
sequence_number: 0,
|
|
};
|
|
|
|
// Try to ping the endpoint
|
|
new.ping()?;
|
|
Ok(new)
|
|
}
|
|
|
|
pub fn request(&mut self, payload: &[u8; 12]) -> Result<Vec<u8>> {
|
|
// Create a blank request
|
|
let mut request = [0x55, 0xaa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
|
|
// Insert the payload
|
|
request[2..14].clone_from_slice(payload);
|
|
|
|
// Insert the sequence number
|
|
request[14..].clone_from_slice(&self.sequence_number.to_le_bytes());
|
|
self.sequence_number += 1;
|
|
|
|
// Send the request
|
|
self.tcp_stream.write_all(&request)?;
|
|
|
|
// Create a buffer and return the response
|
|
let mut buffer = BufReader::new(&self.tcp_stream);
|
|
Ok(buffer.fill_buf()?.to_vec())
|
|
}
|
|
|
|
pub fn ping(&mut self) -> Result<()> {
|
|
// Create a request with a payload of `01 80 00 00 00 00 00 00 00 00 00
|
|
// 00` and get the response
|
|
let response = self.request(&[0x01, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])?;
|
|
|
|
// If the response is not `aa 55 01 01 00 00 00 00 00 00` then return an
|
|
// error
|
|
if response != [0xaa, 0x55, 0x01, 0x01, 0, 0, 0, 0, 0, 0] {
|
|
return Err(Error::new(InvalidData, "Malformed response"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn get_name(&mut self, id: u32) -> Result<Option<String>> {
|
|
// Create a request with a payload of `01 c7 xx xx xx xx 00 00 00 00 14
|
|
// 00`, where `xx xx xx xx` is the employee ID in little endian
|
|
let mut request = [0x01, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0];
|
|
request[2..6].clone_from_slice(&id.to_le_bytes());
|
|
|
|
// Get the response
|
|
let response = self.request(&request)?;
|
|
|
|
// If the response length is right but the header is `01 00 00 00 00 00
|
|
// 00 00` then the request is been succesful but the name was not found
|
|
if response.len() == 34
|
|
&& response[..12] == [0xaa, 0x55, 0x01, 0, 0, 0, 0, 0, 0, 0, 0x55, 0xaa]
|
|
{
|
|
return Ok(None);
|
|
}
|
|
|
|
// If one between the response length or the response header is wrong
|
|
// return an error
|
|
if response.len() != 34
|
|
|| response[..12] != [0xaa, 0x55, 0x01, 0x01, 0, 0, 0, 0, 0, 0, 0x55, 0xaa]
|
|
{
|
|
return Err(Error::new(InvalidData, "Malformed response"));
|
|
}
|
|
|
|
// Get the name as a UTF-8 string and delete the `\0` at the end
|
|
let name = String::from_utf8_lossy(&response[12..22])
|
|
.trim_end_matches('\0')
|
|
.to_string();
|
|
|
|
// Return None if the name is empty, else return the name wrapped into a
|
|
// Some
|
|
Ok(Some(name).filter(|name| !name.is_empty()))
|
|
}
|
|
|
|
pub fn get_total_record_count(&mut self) -> Result<u16> {
|
|
// Create a request with a payload of `01 b4 08 00 00 00 00 00 ff ff 00
|
|
// 00` and get the response
|
|
let response = self.request(&[0x01, 0xb4, 0x08, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0])?;
|
|
|
|
// If one between the response length or the response header is wrong
|
|
// return an error
|
|
if response.len() != 10
|
|
|| response[..4] != [0xaa, 0x55, 0x01, 0x01]
|
|
|| response[6..] != [0u8; 4]
|
|
{
|
|
return Err(Error::new(InvalidData, "Malformed response"));
|
|
}
|
|
|
|
// Convert the payload into a u16 and return it
|
|
let mut record_count_le = [0u8; 2];
|
|
record_count_le.clone_from_slice(&response[4..6]);
|
|
Ok(u16::from_le_bytes(record_count_le))
|
|
}
|
|
|
|
pub fn get_record_bytes(
|
|
&mut self,
|
|
total_records: u16,
|
|
sequence_number: u16,
|
|
) -> Result<Vec<u8>> {
|
|
// Create a request with a payload of `01 a4 00 00 00 00 xx xx xx xx 00
|
|
// 04`, where `xx xx xx xx` changes meaning based on the sequence_number
|
|
// value
|
|
let mut request: [u8; 12] = [0x01, 0xa4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x04];
|
|
|
|
// If sequence_number is 0 then `xx xx xx xx` is `xx xx 00 00`, where
|
|
// `xx xx` is total_records in little endian
|
|
//
|
|
// Else `xx xx xx xx` is `00 00 xx xx`, where `xx xx` is sequence_number
|
|
// in little endian
|
|
if sequence_number == 0 {
|
|
request[6..8].clone_from_slice(&total_records.to_le_bytes());
|
|
} else {
|
|
request[8..10].clone_from_slice(&sequence_number.to_le_bytes());
|
|
}
|
|
|
|
// Get the response
|
|
let response = self.request(&request)?;
|
|
|
|
// If one between the response length, the response header or the last
|
|
// two bits is wrong return an error
|
|
if response.len() != 1038
|
|
|| response[..12] != [0xaa, 0x55, 0x01, 0x01, 0, 0, 0, 0, 0, 0, 0x55, 0xaa]
|
|
|| response[1036..] != [0, 0]
|
|
{
|
|
return Err(Error::new(InvalidData, "Malformed response"));
|
|
}
|
|
|
|
// Return only the payload bits as a vector
|
|
Ok(response[12..1036].to_vec())
|
|
}
|
|
|
|
pub fn into_record_iter(&mut self) -> Result<RecordIterator> {
|
|
RecordIterator::from(self)
|
|
}
|
|
}
|