use crate::error::AdsError;
use crate::proto::ams_address::AmsAddress;
use crate::proto::command_id::CommandID;
use crate::proto::proto_traits::{Command, ReadFrom, WriteTo};
use crate::proto::request::*;
use crate::proto::response::*;
use crate::proto::state_flags::StateFlags;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use std::mem::swap;
const FIX_AMS_HEADER_LEN: u32 = 32;
#[derive(Debug)]
pub struct AmsTcpHeader {
reserved: [u8; 2],
_length: u32,
pub ams_header: AmsHeader,
}
impl WriteTo for AmsTcpHeader {
fn write_to<W: Write>(&self, mut wtr: W) -> io::Result<()> {
wtr.write_all(&self.reserved)?;
wtr.write_u32::<LittleEndian>(self.ams_header.header_len())?;
self.ams_header.write_to(&mut wtr)?;
Ok(())
}
}
impl ReadFrom for AmsTcpHeader {
fn read_from<R: Read>(read: &mut R) -> io::Result<Self> {
let reserved = read.read_u16::<LittleEndian>()?.to_le_bytes();
Ok(AmsTcpHeader {
reserved,
_length: read.read_u32::<LittleEndian>()?,
ams_header: AmsHeader::read_from(read)?,
})
}
}
impl From<AmsHeader> for AmsTcpHeader {
fn from(ams_header: AmsHeader) -> Self {
AmsTcpHeader {
reserved: [0, 0],
_length: ams_header.header_len(),
ams_header,
}
}
}
#[derive(Debug)]
pub struct AmsHeader {
ams_address_targed: AmsAddress,
ams_address_source: AmsAddress,
command_id: CommandID,
state_flags: StateFlags,
length: u32,
ams_ads_error: AdsError,
invoke_id: u32,
data: Vec<u8>,
}
impl WriteTo for AmsHeader {
fn write_to<W: Write>(&self, mut wtr: W) -> io::Result<()> {
self.ams_address_targed.write_to(&mut wtr)?;
self.ams_address_source.write_to(&mut wtr)?;
self.command_id.write_to(&mut wtr)?;
self.state_flags.write_to(&mut wtr)?;
wtr.write_u32::<LittleEndian>(self.length)?;
wtr.write_u32::<LittleEndian>(self.ams_ads_error.as_u32())?;
wtr.write_u32::<LittleEndian>(self.invoke_id)?;
wtr.write_all(&self.data)?;
Ok(())
}
}
impl ReadFrom for AmsHeader {
fn read_from<R: Read>(read: &mut R) -> io::Result<Self> {
let ams_address_targed = AmsAddress::read_from(read)?;
let ams_address_source = AmsAddress::read_from(read)?;
let command_id = CommandID::read_from(read)?;
let state_flags = StateFlags::read_from(read)?;
let length = read.read_u32::<LittleEndian>()?;
let ams_ads_error = AdsError::from(read.read_u32::<LittleEndian>()?);
let invoke_id = read.read_u32::<LittleEndian>()?;
let mut data: Vec<u8> = vec![0; length as usize];
read.read_exact(&mut data)?;
Ok(AmsHeader {
ams_address_targed,
ams_address_source,
command_id,
state_flags,
length,
ams_ads_error,
invoke_id,
data,
})
}
}
impl AmsHeader {
pub fn new(
ams_address_targed: AmsAddress,
ams_address_source: AmsAddress,
state_flags: StateFlags,
invoke_id: u32,
command: impl Command + WriteTo,
) -> Self {
let mut data: Vec<u8> = Vec::with_capacity(16);
command
.write_to(&mut data)
.expect("failed to write request to buffer!");
AmsHeader {
ams_address_targed,
ams_address_source,
command_id: command.command_id(),
state_flags,
length: data.len() as u32,
ams_ads_error: AdsError::ErrNoError,
invoke_id,
data,
}
}
pub fn response(&mut self) -> io::Result<Response> {
if self.state_flags.is_response() {
match self.command_id {
CommandID::Invalid => Err(io::Error::new(
io::ErrorKind::Other,
AdsError::AdsErrDeviceInvalidData,
)),
CommandID::ReadDeviceInfo => Ok(Response::ReadDeviceInfo(
ReadDeviceInfoResponse::read_from(&mut self.data.as_slice())?,
)),
CommandID::Read => Ok(Response::Read(ReadResponse::read_from(
&mut self.data.as_slice(),
)?)),
CommandID::Write => Ok(Response::Write(WriteResponse::read_from(
&mut self.data.as_slice(),
)?)),
CommandID::ReadState => Ok(Response::ReadState(ReadStateResponse::read_from(
&mut self.data.as_slice(),
)?)),
CommandID::WriteControl => Ok(Response::WriteControl(
WriteControlResponse::read_from(&mut self.data.as_slice())?,
)),
CommandID::AddDeviceNotification => Ok(Response::AddDeviceNotification(
AddDeviceNotificationResponse::read_from(&mut self.data.as_slice())?,
)),
CommandID::DeleteDeviceNotification => Ok(Response::DeleteDeviceNotification(
DeleteDeviceNotificationResponse::read_from(&mut self.data.as_slice())?,
)),
CommandID::DeviceNotification => Ok(Response::DeviceNotification(
AdsNotificationStream::read_from(&mut self.data.as_slice())?,
)),
CommandID::ReadWrite => Ok(Response::ReadWrite(ReadWriteResponse::read_from(
&mut self.data.as_slice(),
)?)),
}
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Is not a response but a request!",
))
}
}
pub fn request(&mut self) -> io::Result<Request> {
if !self.state_flags.is_response() {
match self.command_id {
CommandID::Invalid => Err(io::Error::new(
io::ErrorKind::Other,
AdsError::AdsErrDeviceInvalidData,
)),
CommandID::ReadDeviceInfo => {
Ok(Request::ReadDeviceInfo(ReadDeviceInfoRequest::new()))
}
CommandID::Read => Ok(Request::Read(ReadRequest::read_from(
&mut self.data.as_slice(),
)?)),
CommandID::Write => Ok(Request::Write(WriteRequest::read_from(
&mut self.data.as_slice(),
)?)),
CommandID::ReadState => Ok(Request::ReadState(ReadStateRequest::new())),
CommandID::WriteControl => Ok(Request::WriteControl(
WriteControlRequest::read_from(&mut self.data.as_slice())?,
)),
CommandID::AddDeviceNotification => Ok(Request::AddDeviceNotification(
AddDeviceNotificationRequest::read_from(&mut self.data.as_slice())?,
)),
CommandID::DeleteDeviceNotification => Ok(Request::DeleteDeviceNotification(
DeleteDeviceNotificationRequest::read_from(&mut self.data.as_slice())?,
)),
CommandID::DeviceNotification => {
Ok(Request::DeviceNotification(DeviceNotificationRequest::new()))
}
CommandID::ReadWrite => Ok(Request::ReadWrite(ReadWriteRequest::read_from(
&mut self.data.as_slice(),
)?)),
}
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Is not a request but a response!",
))
}
}
pub fn command_id(&self) -> CommandID {
self.command_id
}
pub fn invoke_id(&self) -> u32 {
self.invoke_id
}
pub fn ads_error(&self) -> &AdsError {
&self.ams_ads_error
}
pub fn raw_response_data(&self) -> &[u8] {
&self.data[..]
}
pub fn response_result(&self) -> Option<AdsError> {
if self.data.len() >= 4 {
if let Ok(result) = self.data.as_slice().read_u32::<LittleEndian>() {
return Some(AdsError::from(result));
}
}
None
}
fn header_len(&self) -> u32 {
self.data.len() as u32 + FIX_AMS_HEADER_LEN
}
pub fn data_len(&self) -> u32 {
self.length
}
pub fn update_data(
&mut self,
command: impl Command + WriteTo,
state_flag: StateFlags,
) -> io::Result<()> {
self.command_id = command.command_id();
self.data.clear();
command.write_to(&mut self.data)?;
self.length = self.data.len() as u32;
self.state_flags = state_flag;
Ok(())
}
pub fn swap_address(&mut self) {
swap(&mut self.ams_address_source, &mut self.ams_address_targed);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::proto::ams_address::*;
use std::str::FromStr;
#[test]
fn ams_header_write_to_test() {
let mut buffer: Vec<u8> = Vec::new();
let port = 30000;
let ams_header = AmsHeader::new(
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port),
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port),
StateFlags::resp_default(),
111,
Request::Read(ReadRequest::new(259, 259, 4)),
);
ams_header.write_to(&mut buffer).unwrap();
#[rustfmt::skip]
let compare: Vec<u8> = vec![
192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117,
2, 0,
5, 0,
12, 0, 0, 0,
0, 0, 0, 0,
111, 0, 0, 0,
3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0
];
assert_eq!(compare, buffer);
}
#[test]
fn ams_header_read_from_test() {
#[rustfmt::skip]
let data: Vec<u8> = vec![
192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117,
2, 0,
4, 0,
12, 0, 0, 0,
0, 0, 0, 0,
111, 0, 0, 0,
3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0
];
let ams_header = AmsHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(
ams_header.ams_address_targed.ams_net_id.net_id(),
[192, 168, 1, 1, 1, 1]
);
assert_eq!(
ams_header.ams_address_source.ams_net_id.net_id(),
[192, 168, 1, 1, 1, 2]
);
assert_eq!(ams_header.ams_address_targed.port, 30000);
assert_eq!(ams_header.ams_address_source.port, 30000);
assert_eq!(ams_header.command_id, CommandID::Read);
assert_eq!(ams_header.state_flags.value(), 4);
assert_eq!(ams_header.length, 12, "Wrong data length");
assert_eq!(ams_header.ams_ads_error, AdsError::ErrNoError);
assert_eq!(ams_header.invoke_id, 111);
assert_eq!(ams_header.data, [3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0]);
}
#[test]
fn ams_header_len_test() {
let port = 30000;
let ams_header = AmsHeader::new(
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port),
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port),
StateFlags::req_default(),
111,
Request::Read(ReadRequest::new(259, 259, 4)),
);
assert_eq!(ams_header.header_len(), 44);
}
#[test]
fn ams_header_swap_address() {
let port = 30000;
let mut ams_header = AmsHeader::new(
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port),
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port),
StateFlags::req_default(),
111,
Request::Read(ReadRequest::new(259, 259, 4)),
);
assert_eq!(
ams_header.ams_address_targed,
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port)
);
assert_eq!(
ams_header.ams_address_source,
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port)
);
ams_header.swap_address();
assert_eq!(
ams_header.ams_address_targed,
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port)
);
assert_eq!(
ams_header.ams_address_source,
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port)
);
}
#[test]
fn ams_tcp_header_write_to_test() {
let mut buffer: Vec<u8> = Vec::new();
let port = 30000;
let ams_header = AmsHeader::new(
AmsAddress::new(AmsNetId::from_str("192.168.1.1.1.1").unwrap(), port),
AmsAddress::new(AmsNetId::new(192, 168, 1, 1, 1, 2), port),
StateFlags::req_default(),
111,
Request::Read(ReadRequest::new(259, 259, 4)),
);
let ams_tcp_header = AmsTcpHeader::from(ams_header);
ams_tcp_header.write_to(&mut buffer).unwrap();
#[rustfmt::skip]
let compare: Vec<u8> = vec![
0,0,
44, 0, 0, 0,
192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117,
2, 0,
4, 0,
12, 0, 0, 0,
0, 0, 0, 0,
111, 0, 0, 0,
3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0
];
assert_eq!(compare, buffer);
}
#[test]
fn ams_tcp_header_read_from_test() {
#[rustfmt::skip]
let data: Vec<u8> = vec![
0,0,
44, 0, 0, 0,
192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117,
2, 0,
4, 0,
12, 0, 0, 0,
0, 0, 0, 0,
111, 0, 0, 0,
3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(ams_tcp_header.reserved, [0, 0]);
assert_eq!(ams_tcp_header._length, 44);
assert_eq!(
ams_tcp_header
.ams_header
.ams_address_targed
.ams_net_id
.net_id(),
[192, 168, 1, 1, 1, 1]
);
assert_eq!(
ams_tcp_header
.ams_header
.ams_address_source
.ams_net_id
.net_id(),
[192, 168, 1, 1, 1, 2]
);
assert_eq!(ams_tcp_header.ams_header.ams_address_targed.port, 30000);
assert_eq!(ams_tcp_header.ams_header.ams_address_source.port, 30000);
assert_eq!(ams_tcp_header.ams_header.command_id, CommandID::Read);
assert_eq!(ams_tcp_header.ams_header.state_flags.value(), 4);
assert_eq!(ams_tcp_header.ams_header.length, 12);
assert_eq!(
ams_tcp_header.ams_header.ams_ads_error,
AdsError::ErrNoError
);
assert_eq!(ams_tcp_header.ams_header.invoke_id, 111);
assert_eq!(
ams_tcp_header.ams_header.data,
[3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0]
);
}
#[test]
fn ams_tcp_header_command_id() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(CommandID::Read, ams_tcp_header.ams_header.command_id());
}
#[test]
fn ams_tcp_header_response_data_length() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
let len = ams_tcp_header.ams_header.data_len();
assert_eq!(12, len);
}
#[test]
fn ams_tcp_header_update_data() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let mut ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
let request = Request::ReadDeviceInfo(ReadDeviceInfoRequest::new());
let mut new_data: Vec<u8> = Vec::new();
request.write_to(&mut new_data).unwrap();
ams_tcp_header
.ams_header
.update_data(request, StateFlags::req_default())
.unwrap();
assert_eq!(new_data, ams_tcp_header.ams_header.raw_response_data());
assert_eq!(
ams_tcp_header.ams_header.command_id(),
CommandID::ReadDeviceInfo
);
}
#[test]
fn ams_tcp_header_invoke_id() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(111, ams_tcp_header.ams_header.invoke_id());
}
#[test]
fn ams_tcp_header_ads_error() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(&AdsError::ErrNoError, ams_tcp_header.ams_header.ads_error());
}
#[test]
fn ams_tcp_header_raw_response_data() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(
&[3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0],
ams_tcp_header.ams_header.raw_response_data()
);
}
#[test]
fn ams_tcp_header_responser_result() {
let data: Vec<u8> = vec![
0, 0, 44, 0, 0, 0, 192, 168, 1, 1, 1, 1, 48, 117,
192, 168, 1, 1, 1, 2, 48, 117, 2, 0, 4, 0, 12, 0, 0, 0, 1, 0, 0, 0, 111, 0, 0, 0, 3, 1, 0, 0, 3, 1, 0, 0, 4, 0, 0, 0,
];
let ams_tcp_header = AmsTcpHeader::read_from(&mut data.as_slice()).unwrap();
assert_eq!(
Some(AdsError::ErrUnknowAdsError { error_code: 259 }),
ams_tcp_header.ams_header.response_result()
);
}
}