diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index 093586f31..04a244b52 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -6,6 +6,8 @@ jobs: quick-tests: runs-on: ubuntu-latest steps: + - name: Install libudev + run: sudo apt-get update && sudo apt-get install libudev-dev - uses: actions/checkout@master - uses: actions-rs/toolchain@v1.0.6 with: @@ -22,6 +24,8 @@ jobs: needs: quick-tests runs-on: ubuntu-latest steps: + - name: Install libudev + run: sudo apt-get update && sudo apt-get install libudev-dev - uses: actions/checkout@master - uses: actions-rs/toolchain@v1.0.6 with: diff --git a/Cargo.toml b/Cargo.toml index 4579445b9..0d90bd2e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,7 @@ quote = "1.0" proc-macro2 = "1.0.24" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.64" + +[dev-dependencies] +serialport = "4.3.0" +clap = { version = "3.1.6", features = ["derive"] } diff --git a/README.md b/README.md index ad19b91b0..939d4656c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # ping-rs https://docs.bluerobotics.com/ping-rs/ping_rs/ + +## Using example: + +To run examples use: + +```shell +cargo run --example ping_common /dev/ttyUSB0 115200 +``` + +Should output: +```shell +Parsing user provided values... +Creating serial connection... +Ping protocol request: +Result: Ok(ProtocolVersionStruct { version_major: 1, version_minor: 0, version_patch: 0, reserved: 0 }) +Device information request: +Result: Ok(DeviceInformationStruct { device_type: 1, device_revision: 1, firmware_version_major: 3, firmware_version_minor: 29, firmware_version_patch: 0, reserved: 0 }) +Set device_id request: +Result: Ok(AckStruct { acked_id: 100 }) +Manual request: +Result: Ok(ProtocolMessage { payload_length: 4, message_id: 5, src_device_id: 1, dst_device_id: 0, payload: [1, 0, 0, 0], checksum: 159 }) +``` \ No newline at end of file diff --git a/examples/ping_common.rs b/examples/ping_common.rs new file mode 100644 index 000000000..740d26366 --- /dev/null +++ b/examples/ping_common.rs @@ -0,0 +1,71 @@ +use ping_rs::{common, message::ProtocolMessage, PingDevice}; +use serialport::ClearBuffer; +use std::time::Duration; + +fn main() { + // See mod cli_args bellow. + println!("Parsing user provided values..."); + let (port_name, baud_rate) = cli_args::get_cli_args(); + + println!("Creating serial connection..."); + let port = serialport::new(port_name, baud_rate) + .timeout(Duration::from_millis(100)) + .open() + .expect("Failed to open serial port"); + port.clear(ClearBuffer::All).expect("clear"); + + let mut ping = PingDevice::new(port); + + println!( + "Ping protocol request: \nResult: {:?}", + ping.get_protocol_version() + ); + println!( + "Device information request: \nResult: {:?}", + ping.get_device_information() + ); + println!( + "Set device_id request: \nResult: {:?}", + ping.set_device_id(1) + ); + + let request = + common::Messages::GeneralRequest(common::GeneralRequestStruct { requested_id: 5 }); + let mut package = ProtocolMessage::new(); + package.set_message(&request); + println!("Manual request: \nResult: {:?}", ping.request(package)); +} + +mod cli_args { + use clap::{Arg, Command}; + + pub fn get_cli_args() -> (String, u32) { + let matches = Command::new("Ping Common Example") + .about("Execute generic requests from common module") + .disable_version_flag(true) + .arg( + Arg::new("port") + .help("The device path to a serial port") + .required(true), + ) + .arg( + Arg::new("baud") + .help("The baud rate to connect at") + .use_value_delimiter(false) + .required(true) + .validator(valid_baud), + ) + .get_matches(); + + let port_name = matches.value_of("port").unwrap().to_string(); + let baud_rate = matches.value_of("baud").unwrap().parse::().unwrap(); + + (port_name, baud_rate) + } + + fn valid_baud(val: &str) -> Result<(), String> { + val.parse::() + .map(|_| ()) + .map_err(|_| "Invalid baud rate".into()) + } +} diff --git a/src/decoder.rs b/src/decoder.rs index 0202f5e77..f400072e5 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -94,7 +94,7 @@ impl Decoder { } } - fn reset(&mut self) { + pub fn reset(&mut self) { self.state = DecoderState::AwaitingStart1; self.buffer.clear(); } diff --git a/src/lib.rs b/src/lib.rs index 52ea8f59f..ab62d0519 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ include!(concat!(env!("OUT_DIR"), "/mod.rs")); use message::ProtocolMessage; +use std::io::{Read, Write}; use crate::message::{DeserializeGenericMessage, HEADER}; @@ -9,6 +10,114 @@ use std::convert::TryFrom; pub mod decoder; pub mod message; +#[derive(Debug)] +pub enum PingError { + Io(std::io::Error), + MissingBytes, + ParseError(decoder::ParseError), + UnexpectedStructure, +} + +pub struct PingDevice { + port: D, + decoder: decoder::Decoder, +} + +impl PingDevice { + pub fn new(port: D) -> Self { + Self { + port, + decoder: decoder::Decoder::new(), + } + } + + pub fn read(&mut self, buf: &mut [u8]) -> Result { + match self.port.read(buf) { + Ok(value) => Ok(value), + Err(_e) => Err(PingError::Io(_e)), + } + } + + pub fn write(&mut self, request: Vec) -> Result<(), PingError> { + match self.port.write_all(&request) { + Ok(_e) => { + self.port.flush().unwrap(); + Ok(_e) + } + Err(_e) => Err(PingError::Io(_e)), + } + } + + pub fn request( + &mut self, + request: message::ProtocolMessage, + ) -> Result { + self.write(request.serialized()).unwrap(); + + let mut serial_buf: Vec = vec![0; 20]; + + self.port.read(serial_buf.as_mut_slice()).unwrap(); + + for byte in &serial_buf { + match self.decoder.parse_byte(byte.clone()) { + decoder::DecoderResult::InProgress => continue, + decoder::DecoderResult::Error(_e) => { + return Err(PingError::ParseError(_e)); + } + decoder::DecoderResult::Success(_e) => { + return Ok(_e); + } + } + } + self.decoder.reset(); + Err(PingError::MissingBytes) + } + + pub fn get_protocol_version(&mut self) -> Result { + let request = + common::Messages::GeneralRequest(common::GeneralRequestStruct { requested_id: 5 }); + + let mut package = message::ProtocolMessage::new(); + package.set_message(&request); + + let answer = self.request(package)?; + + match Messages::try_from(&answer) { + Ok(Messages::Common(common::Messages::ProtocolVersion(answer))) => Ok(answer), + _ => Err(PingError::UnexpectedStructure), + } + } + + pub fn get_device_information(&mut self) -> Result { + let request = + common::Messages::GeneralRequest(common::GeneralRequestStruct { requested_id: 4 }); + + let mut package = message::ProtocolMessage::new(); + package.set_message(&request); + + let answer = self.request(package)?; + + match Messages::try_from(&answer) { + Ok(Messages::Common(common::Messages::DeviceInformation(answer))) => Ok(answer), + _ => Err(PingError::UnexpectedStructure), + } + } + + pub fn set_device_id(&mut self, device_id: u8) -> Result { + let request = common::Messages::SetDeviceId(common::SetDeviceIdStruct { device_id }); + + let mut package = ProtocolMessage::new(); + package.set_message(&request); + + let answer = self.request(package)?; + + match Messages::try_from(&answer) { + Ok(Messages::Common(common::Messages::Ack(answer))) => Ok(answer), + _ => Err(PingError::UnexpectedStructure), + } + } +} + pub fn calculate_crc(pack_without_payload: &[u8]) -> u16 { return pack_without_payload .iter()