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

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::Error as SawpError;
use sawp::error::ErrorKind; use sawp::error::ErrorKind;
use sawp::parser::{Direction, Parse}; 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::Deserialize;
use serde_json::{Value, json}; use serde_json::{Value, json};
use std::convert::TryInto; 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( pub fn parse_sawp_message(
msg: &Message, msg: &Message,
map: &FuncMap, map: &FuncMap,
is_response: bool, is_response: bool,
) -> Result<Value, String> { ) -> Result<Value, String> {
// obtain function code and exception flag from msg.function
let mut fn_code = msg.function.raw; let mut fn_code = msg.function.raw;
let is_exception_fn = (fn_code & 0x80) != 0; let is_exception_fn = (fn_code & 0x80) != 0;
if is_exception_fn { if is_exception_fn {
fn_code &= 0x7F; fn_code &= 0x7F;
} }
// Extract a byte-slice to feed generic PDU parser.
// Different Data variants carry bytes differently.
let pdu_bytes = match &msg.data { let pdu_bytes = match &msg.data {
Data::Exception(exc) => { Data::Exception(exc) => {
// Exception contains an exception code byte or similar; convert to vec // Exception typically wraps a u8 code; adapt if Exception is different.
// If Exception type exposes code() or .0, adapt accordingly. // try to extract a u8 from Exception; if it exposes .code() use that.
// Here we try to obtain a single byte; if Exception is an enum with u8 inside: // Here we attempt pattern if Exception(pub u8)
let code_byte = exc.raw; // if Exception(pub u8) let code: u8 = exc.code as u8;
vec![code_byte] vec![code]
} }
Data::Diagnostic { data, .. } => data.clone(), Data::Diagnostic { data, .. } => data.clone(),
Data::MEI { data, .. } => data.clone(), Data::MEI { data, .. } => data.clone(),
Data::Read(read) => { Data::Read(read) => match read {
// If Read provides a bytes() method or inner Vec<u8>, extract it. Read::Request { address, quantity } => {
// Adjust depending on actual Read struct — common shape: Read { byte_count, data: Vec<u8> } let mut v = Vec::with_capacity(4);
// Try common fields: v.extend_from_slice(&address.to_be_bytes());
if let Some(bytes) = try_extract_read_bytes(read) { v.extend_from_slice(&quantity.to_be_bytes());
bytes v
} else {
return Err("unsupported Read variant layout; adapt extraction".into());
} }
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) => { Write::Mask {
if let Some(bytes) = try_extract_write_bytes(write) { address,
bytes and_mask,
} else { or_mask,
return Err("unsupported Write variant layout; adapt extraction".into()); } => {
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 } => { 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 is_response {
if let Some(bytes) = try_extract_read_bytes(read) { match read {
bytes Read::Request { address, quantity } => {
} else { let mut v = Vec::with_capacity(4);
return Err("unsupported Read variant in ReadWrite".into()); v.extend_from_slice(&address.to_be_bytes());
v.extend_from_slice(&quantity.to_be_bytes());
v
}
Read::Response(bytes) => bytes.clone(),
} }
} else { } else {
if let Some(bytes) = try_extract_write_bytes(write) { match write {
bytes Write::MultReq {
} else { address,
return Err("unsupported Write variant in ReadWrite".into()); 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![], Data::Empty => vec![],
}; };
// If exception function: treat as exception
if is_exception_fn { if is_exception_fn {
if pdu_bytes.is_empty() { if pdu_bytes.is_empty() {
return Err("exception message missing exception code".into()); 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 let desc = map
.get(&fn_code) .get(&fn_code)
.ok_or_else(|| format!("unknown function code: {}", 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) parse_with_descriptor(&pdu_bytes, msg.unit_id, msg.function.raw, fields)
} }
/// Generic parser: parse bytes per FieldDescriptor sequence.
fn parse_with_descriptor( fn parse_with_descriptor(
pdu: &[u8], pdu: &[u8],
unit: u8, unit: u8,
@@ -362,15 +365,3 @@ fn insert_mapped(
} }
Ok(()) 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
}