Update parse_sawp_function
Some checks failed
CI / test (push) Failing after 33s

This commit is contained in:
2025-12-22 05:05:46 +00:00
parent 90fcea887e
commit 75329749f7

View File

@@ -3,7 +3,7 @@ use bytes::{Bytes, BytesMut};
use sawp::error::Error as SawpError;
use sawp::error::ErrorKind;
use sawp::parser::{Direction, Parse};
use sawp_modbus::{Data, Message, Modbus};
use sawp_modbus::{Data, Exception, ExceptionCode, Message, Modbus, Read, Write};
use serde::Deserialize;
use serde_json::{Value, json};
use std::convert::TryInto;
@@ -124,109 +124,114 @@ fn get_tcp_data_v6(buf: Bytes) -> Result<Bytes> {
))
}
pub fn get_tcp_payload_from_eth(eth: &EthernetPacket) -> Result<Bytes> {
let pkt = eth.packet(); // returns &[u8]
match eth.get_ethertype() {
EtherTypes::Ipv4 => {
let ipv4 = Ipv4Packet::new(pkt).ok_or_else(|| anyhow!("failed to parse ipv4"))?;
if ipv4.get_next_level_protocol() != IpNextHeaderProtocols::Tcp {
return Err(anyhow!("not a TCP packet"));
}
let ipv4_payload = ipv4.payload();
let tcp = TcpPacket::new(ipv4_payload).ok_or_else(|| anyhow!("failed to parse tcp"))?;
let tcp_payload = tcp.payload();
// compute byte offsets relative to the original packet slice
let base = pkt.as_ptr() as usize;
let start = tcp_payload.as_ptr() as usize - base;
let end = start + tcp_payload.len();
Ok(Bytes::copy_from_slice(&pkt[start..end]).slice(0..tcp_payload.len()))
}
EtherTypes::Ipv6 => {
let ipv6 = Ipv6Packet::new(pkt).ok_or_else(|| anyhow!("failed to parse ipv6"))?;
if ipv6.get_next_header() != IpNextHeaderProtocols::Tcp {
return Err(anyhow!("not a TCP packet"));
}
let ipv6_payload = ipv6.payload();
let tcp = TcpPacket::new(ipv6_payload).ok_or_else(|| anyhow!("failed to parse tcp"))?;
let tcp_payload = tcp.payload();
let base = pkt.as_ptr() as usize;
let start = tcp_payload.as_ptr() as usize - base;
let end = start + tcp_payload.len();
Ok(Bytes::copy_from_slice(&pkt[start..end]).slice(0..tcp_payload.len()))
}
_ => Err(anyhow!("not IPv4/IPv6")),
}
}
/// Top-level parse entry: takes sawp_modbus::Message and descriptor map.
/// Returns JSON object with parsed fields.
pub fn parse_sawp_message(
msg: &Message,
map: &FuncMap,
is_response: bool,
) -> Result<Value, String> {
// obtain function code and exception flag from msg.function
let mut fn_code = msg.function.raw;
let is_exception_fn = (fn_code & 0x80) != 0;
if is_exception_fn {
fn_code &= 0x7F;
}
// Extract a byte-slice to feed generic PDU parser.
// Different Data variants carry bytes differently.
let pdu_bytes = match &msg.data {
Data::Exception(exc) => {
// Exception contains an exception code byte or similar; convert to vec
// If Exception type exposes code() or .0, adapt accordingly.
// Here we try to obtain a single byte; if Exception is an enum with u8 inside:
let code_byte = exc.raw; // if Exception(pub u8)
vec![code_byte]
// Exception typically wraps a u8 code; adapt if Exception is different.
// try to extract a u8 from Exception; if it exposes .code() use that.
// Here we attempt pattern if Exception(pub u8)
let code: u8 = exc.code as u8;
vec![code]
}
Data::Diagnostic { data, .. } => data.clone(),
Data::MEI { data, .. } => data.clone(),
Data::Read(read) => {
// If Read provides a bytes() method or inner Vec<u8>, extract it.
// Adjust depending on actual Read struct — common shape: Read { byte_count, data: Vec<u8> }
// Try common fields:
if let Some(bytes) = try_extract_read_bytes(read) {
bytes
} else {
return Err("unsupported Read variant layout; adapt extraction".into());
Data::Read(read) => match read {
Read::Request { address, quantity } => {
let mut v = Vec::with_capacity(4);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&quantity.to_be_bytes());
v
}
Read::Response(bytes) => bytes.clone(),
},
Data::Write(write) => match write {
Write::MultReq {
address,
quantity,
data,
} => {
let mut v = Vec::with_capacity(4 + data.len());
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&quantity.to_be_bytes());
v.extend_from_slice(data);
v
}
Data::Write(write) => {
if let Some(bytes) = try_extract_write_bytes(write) {
bytes
} else {
return Err("unsupported Write variant layout; adapt extraction".into());
Write::Mask {
address,
and_mask,
or_mask,
} => {
let mut v = Vec::with_capacity(6);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&and_mask.to_be_bytes());
v.extend_from_slice(&or_mask.to_be_bytes());
v
}
Write::Other { address, data } => {
let mut v = Vec::with_capacity(4);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&data.to_be_bytes());
v
}
},
Data::ReadWrite { read, write } => {
// For ReadWrite, decide which side to parse based on is_response.
// If is_response==true, prefer read bytes; else use write bytes.
if is_response {
if let Some(bytes) = try_extract_read_bytes(read) {
bytes
} else {
return Err("unsupported Read variant in ReadWrite".into());
match read {
Read::Request { address, quantity } => {
let mut v = Vec::with_capacity(4);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&quantity.to_be_bytes());
v
}
Read::Response(bytes) => bytes.clone(),
}
} else {
if let Some(bytes) = try_extract_write_bytes(write) {
bytes
} else {
return Err("unsupported Write variant in ReadWrite".into());
match write {
Write::MultReq {
address,
quantity,
data,
} => {
let mut v = Vec::with_capacity(4 + data.len());
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&quantity.to_be_bytes());
v.extend_from_slice(data);
v
}
Write::Mask {
address,
and_mask,
or_mask,
} => {
let mut v = Vec::with_capacity(6);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&and_mask.to_be_bytes());
v.extend_from_slice(&or_mask.to_be_bytes());
v
}
Write::Other { address, data } => {
let mut v = Vec::with_capacity(4);
v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&data.to_be_bytes());
v
}
}
}
Data::ByteVec(v) => v.clone(),
}
Data::ByteVec(b) => b.clone(),
Data::Empty => vec![],
};
// If exception function: treat as exception
if is_exception_fn {
if pdu_bytes.is_empty() {
return Err("exception message missing exception code".into());
@@ -238,7 +243,6 @@ pub fn parse_sawp_message(
}));
}
// Lookup descriptor and parse using generic byte-slice parser
let desc = map
.get(&fn_code)
.ok_or_else(|| format!("unknown function code: {}", fn_code))?;
@@ -252,7 +256,6 @@ pub fn parse_sawp_message(
parse_with_descriptor(&pdu_bytes, msg.unit_id, msg.function.raw, fields)
}
/// Generic parser: parse bytes per FieldDescriptor sequence.
fn parse_with_descriptor(
pdu: &[u8],
unit: u8,
@@ -362,15 +365,3 @@ fn insert_mapped(
}
Ok(())
}
/// Helper extraction functions: adapt these to the real Read/Write structs in your sawp_modbus version.
/// These try common shapes; replace with direct field access if necessary.
fn try_extract_read_bytes<T>(_read: &T) -> Option<Vec<u8>> {
// Replace with actual extraction, for example:
// Some(read.data.clone()) or Some(read.bytes.clone())
None
}
fn try_extract_write_bytes<T>(_write: &T) -> Option<Vec<u8>> {
// Replace with actual extraction
None
}