add sml + powermeter
This commit is contained in:
parent
4e48060bfa
commit
2395014400
12 changed files with 1307 additions and 4 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
Cargo.toml
|
||||
Cargo.lock
|
||||
target
|
||||
.env
|
||||
result
|
||||
|
|
62
Cargo.lock
generated
62
Cargo.lock
generated
|
@ -70,6 +70,14 @@ dependencies = [
|
|||
"stm32f1xx-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "canome_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -108,6 +116,21 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
||||
dependencies = [
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.1.2"
|
||||
|
@ -236,6 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
|
@ -365,6 +389,44 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.200"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.200"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sml_parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"defmt",
|
||||
"heapless",
|
||||
"sml_parser_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sml_parser_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"canome"
|
||||
"canome",
|
||||
"canome_derive",
|
||||
"sml_parser",
|
||||
"sml_parser/sml_parser_derive",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ pub mod can;
|
|||
pub mod contact;
|
||||
pub mod cover;
|
||||
pub mod light;
|
||||
pub mod power;
|
||||
pub mod state;
|
||||
#[cfg(feature = "stm32")]
|
||||
pub mod stm32;
|
||||
|
|
41
canome/src/power.rs
Normal file
41
canome/src/power.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use defmt::Format;
|
||||
|
||||
#[derive(Default, Debug, Format, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PowermeterState {
|
||||
pub total: u32,
|
||||
pub current: u32,
|
||||
}
|
||||
#[cfg(feature = "can")]
|
||||
mod can {
|
||||
use crate::{can::CanDataFormat, Decode, Encode};
|
||||
|
||||
use super::PowermeterState;
|
||||
|
||||
impl Encode<CanDataFormat> for PowermeterState {
|
||||
fn encode(self) -> CanDataFormat {
|
||||
let total = self.total.to_be_bytes();
|
||||
let current = self.current.to_be_bytes();
|
||||
CanDataFormat::new(&[
|
||||
total[0], total[1], total[2], total[3], current[0], current[1], current[2],
|
||||
current[3],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode<CanDataFormat> for PowermeterState {
|
||||
fn decode(value: CanDataFormat) -> Result<Self, CanDataFormat>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let data = value.data();
|
||||
if data.len() != 8 {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
let total = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
|
||||
let current = u32::from_be_bytes([data[4], data[4], data[5], data[6]]);
|
||||
|
||||
Ok(Self { total, current })
|
||||
}
|
||||
}
|
||||
}
|
11
canome_derive/Cargo.toml
Normal file
11
canome_derive/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "canome_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "2.0"
|
||||
quote = "1.0"
|
62
canome_derive/src/lib.rs
Normal file
62
canome_derive/src/lib.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parenthesized, Data, Expr, Fields, GenericParam, Path};
|
||||
|
||||
#[proc_macro_derive(Channel)]
|
||||
pub fn derive_channel(input: TokenStream) -> TokenStream {
|
||||
let ast = syn::parse(input).unwrap();
|
||||
impl_channel(&ast)
|
||||
}
|
||||
fn impl_channel(input: &syn::DeriveInput) -> TokenStream {
|
||||
let name = &input.ident;
|
||||
|
||||
let canome_attr = input
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|x| x.path().is_ident("canome"))
|
||||
.unwrap();
|
||||
|
||||
let mut group: Expr = syn::parse_quote!(());
|
||||
canome_attr
|
||||
.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("group") {
|
||||
let content;
|
||||
parenthesized!(content in meta.input );
|
||||
group = content.parse()?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Err(meta.error("unrecognized canome attr"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut generics = input.generics.clone();
|
||||
let tx_cap_param = syn::parse_quote!(const TX_CAP: usize);
|
||||
generics.params.push(GenericParam::Const(tx_cap_param));
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
match &input.data {
|
||||
Data::Struct(str) => match &str.fields {
|
||||
Fields::Unnamed(fields) => {
|
||||
if fields.unnamed.len() != 1 {
|
||||
unimplemented!();
|
||||
}
|
||||
let field = fields.unnamed.first().unwrap();
|
||||
|
||||
let field_type = &field.ty;
|
||||
|
||||
let group = "";
|
||||
|
||||
quote! {
|
||||
impl #impl_generics Channel<BxCanBus<TX_CAP>> for #name #ty_generics #where_clause{
|
||||
const ID: BxCanId = #group;
|
||||
type State = #field_type;
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
10
sml_parser/Cargo.toml
Normal file
10
sml_parser/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "sml_parser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
heapless = { version = "0.8.0", features = ["serde"]}
|
||||
sml_parser_derive = {path = "sml_parser_derive"}
|
||||
crc = "3.2"
|
||||
defmt = "0.3.6"
|
11
sml_parser/sml_parser_derive/Cargo.toml
Normal file
11
sml_parser/sml_parser_derive/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "sml_parser_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
52
sml_parser/sml_parser_derive/src/lib.rs
Normal file
52
sml_parser/sml_parser_derive/src/lib.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Data;
|
||||
|
||||
#[proc_macro_derive(SmlType)]
|
||||
pub fn derive_sml_type(input: TokenStream) -> TokenStream {
|
||||
let ast = syn::parse(input).unwrap();
|
||||
impl_syl_type(&ast)
|
||||
}
|
||||
|
||||
fn impl_syl_type(ast: &syn::DeriveInput) -> TokenStream {
|
||||
let name = &ast.ident;
|
||||
match &ast.data {
|
||||
Data::Struct(s) => {
|
||||
let fields = &s
|
||||
.fields
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let name = &x.ident.as_ref().unwrap();
|
||||
quote! {
|
||||
#name: buf.parse()?
|
||||
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let num_fields = s.fields.len();
|
||||
|
||||
let lifetimes = ast.generics.lifetimes().collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
impl<'a> SmlType<'a> for #name<#(#lifetimes),*> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != #num_fields {
|
||||
return Err(Error::ExpectedStruct(core::any::type_name::<Self>()));
|
||||
}
|
||||
Ok(Self {
|
||||
#(#fields),*
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
805
sml_parser/src/lib.rs
Normal file
805
sml_parser/src/lib.rs
Normal file
|
@ -0,0 +1,805 @@
|
|||
#![no_std]
|
||||
use core::fmt::Debug;
|
||||
use core::slice::SliceIndex;
|
||||
use defmt::Format;
|
||||
use primitives::{FieldType, ListIter, ListOf, OctetString, TypeLengthField};
|
||||
use sml_parser_derive::SmlType;
|
||||
|
||||
pub mod primitives;
|
||||
|
||||
const CRC16: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
pub enum Error {
|
||||
ExpectedSigned,
|
||||
ExpectedUnsigned,
|
||||
ExpectedOctetString,
|
||||
ExpectedBool,
|
||||
ExpectedList,
|
||||
ExpectedSmlMessage,
|
||||
ExpectedEndOfSmlMsg,
|
||||
ExpectedSmlPublicOpenReq,
|
||||
ExpectedSmlTimestampLocal,
|
||||
ExpectedSmlPublicCloseReq,
|
||||
ExpectedSmlPublicCloseRes,
|
||||
|
||||
UnsupportedTLF,
|
||||
|
||||
ExpectedStruct(&'static str),
|
||||
ExpectedEnum(&'static str),
|
||||
ExpectedOption,
|
||||
InvalidCRC,
|
||||
|
||||
ExpectedEscapeSeq,
|
||||
InvalidPadding,
|
||||
|
||||
ExpectedAbortOnError,
|
||||
|
||||
Custom,
|
||||
}
|
||||
|
||||
pub trait SmlType<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParserContext<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> ParserContext<'a> {
|
||||
pub fn parse<T: SmlType<'a>>(&mut self) -> Result<T, Error> {
|
||||
T::parse(self)
|
||||
}
|
||||
pub fn peek<I: SliceIndex<[u8]>>(&self, range: I) -> Result<&'a I::Output, Error> {
|
||||
Ok(&self.0[range])
|
||||
}
|
||||
|
||||
pub fn peek_until(&self, other: &ParserContext<'a>) -> Result<&'a [u8], Error> {
|
||||
if let Some(offset) = other.index_within(self) {
|
||||
self.peek(..offset)
|
||||
} else {
|
||||
self.peek(..)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&mut self, length: usize) -> Result<&'a [u8], Error> {
|
||||
let val = &self.0[..length];
|
||||
self.0 = &self.0[length..];
|
||||
Ok(val)
|
||||
}
|
||||
pub fn move_fwd(&mut self, dist: usize) -> Result<(), Error> {
|
||||
self.0 = &self.0[dist..];
|
||||
Ok(())
|
||||
}
|
||||
fn index_within(&self, outer: &Self) -> Option<usize> {
|
||||
let outer_beg = outer.0.as_ptr() as usize;
|
||||
let outer_end = outer_beg + outer.0.len();
|
||||
let inner_beg = self.0.as_ptr() as usize;
|
||||
let inner_end = inner_beg + self.0.len();
|
||||
if inner_beg >= outer_beg && inner_end <= outer_end {
|
||||
Some(inner_beg - outer_beg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// representation of an SML File that holds multiple [`SmlMessage`].
|
||||
pub struct SmlFile<'a> {
|
||||
data: &'a [u8],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for SmlFile<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let crc_buf = buf.clone();
|
||||
if buf.peek(..8)? != [0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01] {
|
||||
return Err(Error::ExpectedEscapeSeq);
|
||||
}
|
||||
buf.move_fwd(8)?;
|
||||
let prev_buf = buf.clone();
|
||||
let mut len = 0;
|
||||
while ![0x00, 0x1b].contains(buf.peek(0)?) {
|
||||
SmlMessage::parse(buf)?;
|
||||
len += 1;
|
||||
}
|
||||
let data = prev_buf.peek_until(buf)?;
|
||||
|
||||
let mut fill = 0;
|
||||
while *buf.peek(0)? == 0 {
|
||||
fill += 1;
|
||||
buf.move_fwd(1)?;
|
||||
}
|
||||
if buf.peek(..5)? != [0x1b, 0x1b, 0x1b, 0x1b, 0x1a] {
|
||||
return Err(Error::ExpectedEscapeSeq);
|
||||
}
|
||||
buf.move_fwd(5)?;
|
||||
let should_fill = *buf.peek(0)?;
|
||||
buf.move_fwd(1)?;
|
||||
if fill != should_fill {
|
||||
return Err(Error::InvalidPadding);
|
||||
}
|
||||
|
||||
let crc_data = crc_buf.peek_until(buf)?;
|
||||
|
||||
let mut should_crc = [0; 2];
|
||||
should_crc.copy_from_slice(buf.peek(..2)?);
|
||||
let should_crc = u16::from_be_bytes(should_crc);
|
||||
|
||||
let crc = CRC16.checksum(crc_data).swap_bytes();
|
||||
if crc != should_crc {
|
||||
return Err(Error::InvalidCRC);
|
||||
}
|
||||
|
||||
Ok(Self { data, len })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SmlFile<'a> {
|
||||
pub fn iter(&self) -> ListIter<'a, SmlMessage<'a>> {
|
||||
ListIter::new(self.data, self.len)
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug for SmlFile<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("SmlFile")
|
||||
.field("len", &self.len)
|
||||
.field("messages", &self.iter())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// representation of the SML_Message Sequence
|
||||
#[derive(Debug)]
|
||||
pub struct SmlMessage<'a> {
|
||||
pub transaction_id: OctetString<'a>,
|
||||
pub group_no: u8,
|
||||
pub abort_on_error: AbortOnError,
|
||||
pub message_body: SmlMessageBody<'a>,
|
||||
pub crc16: u16,
|
||||
pub end_of_sml_msg: EndOfSmlMsg,
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for SmlMessage<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let pre_buf = buf.clone();
|
||||
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 6usize {
|
||||
return Err(Error::ExpectedStruct(core::any::type_name::<Self>()));
|
||||
}
|
||||
|
||||
let transaction_id = buf.parse()?;
|
||||
let group_no = buf.parse()?;
|
||||
let abort_on_error = buf.parse()?;
|
||||
let message_body = buf.parse()?;
|
||||
|
||||
let data_for_crc = pre_buf.peek_until(buf)?;
|
||||
|
||||
let crc16 = buf.parse()?;
|
||||
|
||||
if CRC16.checksum(data_for_crc).swap_bytes() != crc16 {
|
||||
return Err(Error::InvalidCRC);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
transaction_id,
|
||||
group_no,
|
||||
abort_on_error,
|
||||
message_body,
|
||||
crc16,
|
||||
end_of_sml_msg: buf.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SmlMessageBody<'a> {
|
||||
OpenRequest(SmlPublicOpenReq<'a>),
|
||||
OpenResponse(SmlPublicOpenRes<'a>),
|
||||
CloseRequest(SmlPublicCloseReq<'a>),
|
||||
CloseResponse(SmlPublicCloseRes<'a>),
|
||||
GetProfilePackRequest(SmlGetProfilePackReq<'a>),
|
||||
GetProfilePackResponse(SmlGetProfilePackRes<'a>),
|
||||
GetProfileListRequest(SmlGetProfileListReq<'a>),
|
||||
GetProfileListResponse(SmlGetProfileListRes<'a>),
|
||||
GetProcParameterRequest(SmlGetProcParameterReq<'a>),
|
||||
GetProcParameterResponse(SmlGetProcParameterRes<'a>),
|
||||
SetProcParameterRequest(SmlSetProcParameterReq<'a>),
|
||||
GetListRequest(SmlGetListReq<'a>),
|
||||
GetListResponse(SmlGetListRes<'a>),
|
||||
GetCosemRequest(SmlGetCosemReq<'a>),
|
||||
GetCosemResponse(SmlGetCosemRes<'a>),
|
||||
SetCosemRequest(SmlSetCosemReq<'a>),
|
||||
SetCosemResponse(SmlSetCosemRes<'a>),
|
||||
ActionCosemRequest(SmlActionCosemReq<'a>),
|
||||
ActionCosemResponse(SmlActionCosemRes<'a>),
|
||||
AttentionResponse(SmlAttentionRes<'a>),
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for SmlMessageBody<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
use SmlMessageBody as S;
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 2 {
|
||||
return Err(Error::ExpectedEnum(core::any::type_name::<Self>()));
|
||||
}
|
||||
let disc: u32 = buf.parse()?;
|
||||
|
||||
match disc {
|
||||
0x00000100 => buf.parse().map(S::OpenRequest),
|
||||
0x00000101 => buf.parse().map(S::OpenResponse),
|
||||
0x00000200 => buf.parse().map(S::CloseRequest),
|
||||
0x00000201 => buf.parse().map(S::CloseResponse),
|
||||
0x00000300 => buf.parse().map(S::GetProfilePackRequest),
|
||||
0x00000301 => buf.parse().map(S::GetProfilePackResponse),
|
||||
0x00000400 => buf.parse().map(S::GetProfileListRequest),
|
||||
0x00000401 => buf.parse().map(S::GetProfileListResponse),
|
||||
0x00000500 => buf.parse().map(S::GetProcParameterRequest),
|
||||
0x00000501 => buf.parse().map(S::GetProcParameterResponse),
|
||||
0x00000600 => buf.parse().map(S::SetProcParameterRequest),
|
||||
0x00000700 => buf.parse().map(S::GetListRequest),
|
||||
0x00000701 => buf.parse().map(S::GetListResponse),
|
||||
0x00000800 => buf.parse().map(S::GetCosemRequest),
|
||||
0x00000801 => buf.parse().map(S::GetCosemResponse),
|
||||
0x00000900 => buf.parse().map(S::SetCosemRequest),
|
||||
0x00000901 => buf.parse().map(S::SetCosemResponse),
|
||||
0x00000a00 => buf.parse().map(S::ActionCosemRequest),
|
||||
0x00000a01 => buf.parse().map(S::ActionCosemResponse),
|
||||
0x0000ff01 => buf.parse().map(S::AttentionResponse),
|
||||
_ => Err(Error::ExpectedEnum(core::any::type_name::<Self>())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlPublicOpenReq<'a> {
|
||||
pub codepage: Option<OctetString<'a>>,
|
||||
pub client_id: OctetString<'a>,
|
||||
pub req_file_id: OctetString<'a>,
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub sml_version: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlPublicOpenRes<'a> {
|
||||
pub codepage: Option<OctetString<'a>>,
|
||||
pub client_id: Option<OctetString<'a>>,
|
||||
pub req_file_id: OctetString<'a>,
|
||||
pub server_id: OctetString<'a>,
|
||||
pub ref_time: Option<SmlTime>,
|
||||
pub sml_version: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlPublicCloseReq<'a> {
|
||||
pub global_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlPublicCloseRes<'a> {
|
||||
pub global_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProfilePackReq<'a> {
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub with_rawdata: Option<bool>,
|
||||
pub begin_time: Option<SmlTime>,
|
||||
pub end_time: Option<SmlTime>,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub object_list: Option<ListOf<'a, SmlObjReqEntry<'a>>>,
|
||||
pub das_details: Option<SmlTree<'a>>,
|
||||
}
|
||||
pub type SmlObjReqEntry<'a> = OctetString<'a>;
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProfilePackRes<'a> {
|
||||
pub server_id: OctetString<'a>,
|
||||
pub act_time: SmlTime,
|
||||
pub reg_period: u32,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub header_list: ListOf<'a, SmlProfObjHeaderEntry<'a>>,
|
||||
pub period_list: ListOf<'a, SmlProfObjPeriodEntry<'a>>,
|
||||
pub rawdata: Option<OctetString<'a>>,
|
||||
pub profile_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlProfObjHeaderEntry<'a> {
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub unit: SmlUnit,
|
||||
pub scaler: u8,
|
||||
}
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlProfObjPeriodEntry<'a> {
|
||||
pub val_time: SmlTime,
|
||||
pub status: u64,
|
||||
pub value_list: ListOf<'a, SmlValueEntry<'a>>,
|
||||
pub period_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlValueEntry<'a> {
|
||||
pub value: SmlValue<'a>,
|
||||
pub value_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
pub type SmlUnit = u8;
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProfileListReq<'a> {
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub with_rawdata: Option<bool>,
|
||||
pub begin_time: Option<SmlTime>,
|
||||
pub end_time: Option<SmlTime>,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub object_list: Option<ListOf<'a, SmlObjReqEntry<'a>>>,
|
||||
pub das_details: Option<SmlTree<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProfileListRes<'a> {
|
||||
pub server_id: OctetString<'a>,
|
||||
pub act_time: SmlTime,
|
||||
pub reg_period: u32,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub val_time: SmlTime,
|
||||
pub status: u64,
|
||||
pub period_list: ListOf<'a, SmlPeriodEntry<'a>>,
|
||||
pub rawdata: Option<OctetString<'a>>,
|
||||
pub period_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlPeriodEntry<'a> {
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub unit: SmlUnit,
|
||||
pub scaler: i8,
|
||||
pub value: SmlValue<'a>,
|
||||
pub value_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProcParameterReq<'a> {
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub attribute: Option<OctetString<'a>>,
|
||||
}
|
||||
|
||||
pub type SmlTreePath<'a> = ListOf<'a, OctetString<'a>>;
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetProcParameterRes<'a> {
|
||||
pub server_id: OctetString<'a>,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub paramter_tree: SmlTree<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlTree<'a> {
|
||||
pub parameter_name: OctetString<'a>,
|
||||
pub parameter_value: Option<SmlProcParValue<'a>>,
|
||||
pub child_list: Option<ListOf<'a, SmlTree<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlSetProcParameterReq<'a> {
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub paramter_tree_path: SmlTreePath<'a>,
|
||||
pub paramter_tree: SmlTree<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlAttentionRes<'a> {
|
||||
pub server_id: OctetString<'a>,
|
||||
pub attention_no: OctetString<'a>,
|
||||
pub attention_msg: Option<OctetString<'a>>,
|
||||
pub attention_details: Option<SmlTree<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetListReq<'a> {
|
||||
pub client_id: OctetString<'a>,
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub list_name: Option<OctetString<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetListRes<'a> {
|
||||
pub client_id: Option<OctetString<'a>>,
|
||||
pub server_id: OctetString<'a>,
|
||||
pub list_name: OctetString<'a>,
|
||||
pub act_sensor_time: Option<SmlTime>,
|
||||
pub val_list: ListOf<'a, SmlListEntry<'a>>,
|
||||
pub list_signature: Option<SmlSignature<'a>>,
|
||||
pub act_gateway_time: Option<SmlTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlListEntry<'a> {
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub status: Option<SmlStatus>,
|
||||
pub val_time: Option<SmlTime>,
|
||||
pub unit: Option<SmlUnit>,
|
||||
pub scaler: Option<i8>,
|
||||
pub value: SmlValue<'a>,
|
||||
pub value_signature: Option<SmlSignature<'a>>,
|
||||
}
|
||||
|
||||
pub type SmlStatus = u64;
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetCosemReq<'a> {
|
||||
pub client_id: OctetString<'a>,
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub attribute_index_list: Option<ListOf<'a, SmlCosemAttributeDesc<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlCosemAttributeDesc<'a> {
|
||||
pub attribute_index: i16,
|
||||
pub selective_access_descriptor: Option<SmlCosemSelAccessDesc<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlCosemSelAccessDesc<'a> {
|
||||
pub access_selector: u8,
|
||||
pub access_parameters: SmlCosemValue<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlGetCosemRes<'a> {
|
||||
pub client_id: Option<OctetString<'a>>,
|
||||
pub server_id: OctetString<'a>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub attribute_list: ListOf<'a, SmlCosemAttribute<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlCosemAttribute<'a> {
|
||||
pub attribute_description: SmlCosemAttributeDesc<'a>,
|
||||
pub attribute_content: SmlCosemAttributeContent<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlSetCosemReq<'a> {
|
||||
pub client_id: OctetString<'a>,
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub attribute_list: ListOf<'a, SmlCosemAttribute<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlSetCosemRes<'a> {
|
||||
pub client_id: Option<OctetString<'a>>,
|
||||
pub server_id: OctetString<'a>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub attribute_list: Option<ListOf<'a, SmlCosemAttribute<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlActionCosemReq<'a> {
|
||||
pub client_id: OctetString<'a>,
|
||||
pub server_id: Option<OctetString<'a>>,
|
||||
pub username: Option<OctetString<'a>>,
|
||||
pub password: Option<OctetString<'a>>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub service_index: u8,
|
||||
pub service_parameter: Option<SmlCosemValue<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlActionCosemRes<'a> {
|
||||
pub client_id: Option<OctetString<'a>>,
|
||||
pub server_id: OctetString<'a>,
|
||||
pub obj_name: OctetString<'a>,
|
||||
pub class_id: i16,
|
||||
pub class_version: i16,
|
||||
pub attribute_list: Option<ListOf<'a, SmlCosemAttribute<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EndOfSmlMsg;
|
||||
impl<'a> SmlType<'a> for EndOfSmlMsg {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if *buf.peek(0)? == 0 {
|
||||
buf.move_fwd(1)?;
|
||||
Ok(Self)
|
||||
} else {
|
||||
Err(Error::ExpectedEndOfSmlMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AbortOnError {
|
||||
Continue,
|
||||
ContinueWithNextGroup,
|
||||
AbortAfterGroup,
|
||||
Abort,
|
||||
}
|
||||
impl<'a> SmlType<'a> for AbortOnError {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let disc: u8 = buf.read(1)?[0];
|
||||
Ok(match disc {
|
||||
0x00 => Self::Continue,
|
||||
0x01 => Self::ContinueWithNextGroup,
|
||||
0x02 => Self::AbortAfterGroup,
|
||||
0x03 => Self::Abort,
|
||||
_ => return Err(Error::ExpectedAbortOnError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
pub enum SmlValue<'a> {
|
||||
Boolean(bool),
|
||||
OctetString(OctetString<'a>),
|
||||
Integer8(i8),
|
||||
Integer16(i16),
|
||||
Integer32(i32),
|
||||
Integer64(i64),
|
||||
Unsigned8(u8),
|
||||
Unsigned16(u16),
|
||||
Unsigned32(u32),
|
||||
Unsigned64(u64),
|
||||
List(SmlListType),
|
||||
}
|
||||
impl<'a> SmlType<'a> for SmlValue<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::peek(buf)?;
|
||||
match (tlf.kind(), tlf.data_len()) {
|
||||
(FieldType::Boolean, _) => buf.parse().map(Self::Boolean),
|
||||
(FieldType::OctetString, _) => buf.parse().map(Self::OctetString),
|
||||
(FieldType::Integer, 1) => buf.parse().map(Self::Integer8),
|
||||
(FieldType::Integer, 2) => buf.parse().map(Self::Integer16),
|
||||
(FieldType::Integer, x) if x <= 4 => buf.parse().map(Self::Integer32),
|
||||
(FieldType::Integer, x) if x <= 8 => buf.parse().map(Self::Integer64),
|
||||
(FieldType::Unsigned, 1) => buf.parse().map(Self::Unsigned8),
|
||||
(FieldType::Unsigned, 2) => buf.parse().map(Self::Unsigned16),
|
||||
(FieldType::Unsigned, x) if x <= 4 => buf.parse().map(Self::Unsigned32),
|
||||
(FieldType::Unsigned, x) if x <= 8 => buf.parse().map(Self::Unsigned64),
|
||||
(FieldType::List, _) => buf.parse().map(Self::List),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
pub enum SmlListType {
|
||||
Time(SmlTime),
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for SmlListType {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
use SmlListType as S;
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 2 {
|
||||
return Err(Error::ExpectedEnum(core::any::type_name::<Self>()));
|
||||
}
|
||||
let disc: u8 = buf.parse()?;
|
||||
|
||||
match disc {
|
||||
0x01 => buf.parse().map(S::Time),
|
||||
_ => Err(Error::ExpectedEnum(core::any::type_name::<Self>())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SmlProcParValue<'a> {
|
||||
Value(SmlValue<'a>),
|
||||
PeriodEntry(SmlPeriodEntry<'a>),
|
||||
TupelEntry(SmlTupelEntry<'a>),
|
||||
Time(SmlTime),
|
||||
ListEntry(SmlListEntry<'a>),
|
||||
}
|
||||
impl<'a> SmlType<'a> for SmlProcParValue<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
use SmlProcParValue as S;
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 2 {
|
||||
return Err(Error::ExpectedEnum(core::any::type_name::<Self>()));
|
||||
}
|
||||
let disc: u8 = buf.parse()?;
|
||||
|
||||
match disc {
|
||||
0x01 => buf.parse().map(S::Value),
|
||||
0x02 => buf.parse().map(S::PeriodEntry),
|
||||
0x03 => buf.parse().map(S::TupelEntry),
|
||||
0x04 => buf.parse().map(S::Time),
|
||||
0x05 => buf.parse().map(S::ListEntry),
|
||||
_ => Err(Error::ExpectedEnum(core::any::type_name::<Self>())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, SmlType)]
|
||||
pub struct SmlTupelEntry<'a> {
|
||||
pub server_id: OctetString<'a>,
|
||||
pub sec_index: SmlTime,
|
||||
pub status: u64,
|
||||
pub unit_pa: SmlUnit,
|
||||
pub scaler_pa: i8,
|
||||
pub value_pa: i64,
|
||||
pub unit_r1: SmlUnit,
|
||||
pub scaler_r1: i8,
|
||||
pub value_r1: i64,
|
||||
pub unit_r4: SmlUnit,
|
||||
pub scaler_r4: i8,
|
||||
pub value_r4: i64,
|
||||
pub signature_pa_r1_r4: OctetString<'a>,
|
||||
pub unit_ma: SmlUnit,
|
||||
pub scaler_ma: i8,
|
||||
pub value_ma: i64,
|
||||
pub unit_r2: SmlUnit,
|
||||
pub scaler_r2: i8,
|
||||
pub value_r2: i64,
|
||||
pub unit_r3: SmlUnit,
|
||||
pub scaler_r3: i8,
|
||||
pub value_r3: i64,
|
||||
pub signature_ma_r2_r3: OctetString<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
pub enum SmlTime {
|
||||
SecIndex(u32),
|
||||
Timestamp(SmlTimestamp),
|
||||
TimestampLocal(SmlTimestampLocal),
|
||||
}
|
||||
impl<'a> SmlType<'a> for SmlTime {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
use SmlTime as S;
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 2 {
|
||||
return Err(Error::ExpectedEnum(core::any::type_name::<Self>()));
|
||||
}
|
||||
let disc: u8 = buf.parse()?;
|
||||
|
||||
match disc {
|
||||
0x01 => buf.parse().map(S::SecIndex),
|
||||
0x02 => buf.parse().map(S::Timestamp),
|
||||
0x03 => buf.parse().map(S::TimestampLocal),
|
||||
_ => Err(Error::ExpectedEnum(core::any::type_name::<Self>())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type SmlTimestamp = u32;
|
||||
|
||||
#[derive(Debug, SmlType, Format)]
|
||||
pub struct SmlTimestampLocal {
|
||||
pub timestamp: SmlTimestamp,
|
||||
pub local_offset: u16,
|
||||
pub season_time_offset: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SmlCosemValue<'a> {
|
||||
NullData,
|
||||
Boolean(bool),
|
||||
BitString,
|
||||
OctetString(OctetString<'a>),
|
||||
Integer8(i8),
|
||||
Integer16(i16),
|
||||
Integer32(i32),
|
||||
Integer64(i64),
|
||||
Unsigned8(u8),
|
||||
Unsigned16(u16),
|
||||
Unsigned32(u32),
|
||||
Unsigned64(u64),
|
||||
Struct(ListOf<'a, SmlCosemValue<'a>>),
|
||||
Array(ListOf<'a, SmlCosemValue<'a>>),
|
||||
}
|
||||
impl<'a> SmlType<'a> for SmlCosemValue<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::peek(buf)?;
|
||||
match (tlf.kind(), tlf.data_len()) {
|
||||
(FieldType::Boolean, _) => buf.parse().map(Self::Boolean),
|
||||
(FieldType::OctetString, _) => buf.parse().map(Self::OctetString),
|
||||
(FieldType::Integer, 1) => buf.parse().map(Self::Integer8),
|
||||
(FieldType::Integer, 2) => buf.parse().map(Self::Integer16),
|
||||
(FieldType::Integer, 4) => buf.parse().map(Self::Integer32),
|
||||
(FieldType::Integer, 8) => buf.parse().map(Self::Integer64),
|
||||
(FieldType::Unsigned, 1) => buf.parse().map(Self::Unsigned8),
|
||||
(FieldType::Unsigned, 2) => buf.parse().map(Self::Unsigned16),
|
||||
(FieldType::Unsigned, 4) => buf.parse().map(Self::Unsigned32),
|
||||
(FieldType::Unsigned, 8) => buf.parse().map(Self::Unsigned64),
|
||||
(FieldType::List, _) => buf.parse().map(Self::Struct),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SmlCosemAttributeContent<'a> {
|
||||
Data(SmlCosemValue<'a>),
|
||||
DataAccessResult(u8),
|
||||
}
|
||||
impl<'a> SmlType<'a> for SmlCosemAttributeContent<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
use SmlCosemAttributeContent as S;
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind() != FieldType::List || tlf.len() != 2 {
|
||||
return Err(Error::ExpectedEnum(core::any::type_name::<Self>()));
|
||||
}
|
||||
let disc: u8 = buf.parse()?;
|
||||
|
||||
match disc {
|
||||
0x01 => buf.parse().map(S::Data),
|
||||
0x02 => buf.parse().map(S::DataAccessResult),
|
||||
_ => Err(Error::ExpectedEnum(core::any::type_name::<Self>())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type SmlSignature<'a> = OctetString<'a>;
|
247
sml_parser/src/primitives.rs
Normal file
247
sml_parser/src/primitives.rs
Normal file
|
@ -0,0 +1,247 @@
|
|||
use core::{fmt::Debug, marker::PhantomData};
|
||||
|
||||
use defmt::Format;
|
||||
|
||||
use crate::{Error, ParserContext, SmlType};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypeLengthField {
|
||||
header_len: usize,
|
||||
len: usize,
|
||||
kind: FieldType,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum FieldType {
|
||||
OctetString,
|
||||
Boolean,
|
||||
Integer,
|
||||
Unsigned,
|
||||
List,
|
||||
Reserved,
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for TypeLengthField {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error> {
|
||||
let val = Self::peek(buf)?;
|
||||
buf.move_fwd(val.header_len)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
impl TypeLengthField {
|
||||
pub fn peek(buf: &ParserContext) -> Result<Self, Error> {
|
||||
let mut len: usize = 0;
|
||||
let mut header_len = 0;
|
||||
for b in buf.peek(0..)? {
|
||||
let additional = (*b >> 7) & 1 == 1;
|
||||
header_len += 1;
|
||||
if additional {
|
||||
len += (*b & 0b1111) as usize;
|
||||
len <<= 4;
|
||||
} else {
|
||||
len += (*b & 0b1111) as usize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let kind = (*buf.peek(0)? >> 4) & 0b111;
|
||||
let kind = match kind {
|
||||
0b000 => FieldType::OctetString,
|
||||
0b100 => FieldType::Boolean,
|
||||
0b101 => FieldType::Integer,
|
||||
0b110 => FieldType::Unsigned,
|
||||
0b111 => FieldType::List,
|
||||
0b001 => FieldType::Reserved,
|
||||
0b010 => FieldType::Reserved,
|
||||
0b011 => FieldType::Reserved,
|
||||
_ => return Err(Error::UnsupportedTLF),
|
||||
};
|
||||
Ok(Self {
|
||||
header_len,
|
||||
len,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
pub fn data_len(&self) -> usize {
|
||||
self.len - self.header_len
|
||||
}
|
||||
pub fn header_len(&self) -> usize {
|
||||
self.header_len
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
pub fn kind(&self) -> FieldType {
|
||||
self.kind
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
pub struct OctetString<'a>(pub &'a [u8]);
|
||||
impl<'a> SmlType<'a> for OctetString<'a> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind != FieldType::OctetString {
|
||||
return Err(Error::ExpectedOctetString);
|
||||
}
|
||||
Ok(Self(buf.read(tlf.data_len())?))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListOf<'a, T: SmlType<'a>> {
|
||||
data: &'a [u8],
|
||||
len: usize,
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: SmlType<'a>> SmlType<'a> for ListOf<'a, T> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind != FieldType::List {
|
||||
return Err(Error::ExpectedList);
|
||||
}
|
||||
|
||||
let pre_buf = buf.clone();
|
||||
|
||||
for res in (0..tlf.len).map(|_| T::parse(buf)) {
|
||||
res?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
data: pre_buf.peek_until(buf)?,
|
||||
len: tlf.len,
|
||||
_t: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'a, T: SmlType<'a> + Debug> Debug for ListOf<'a, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("ListOf")
|
||||
.field("len", &self.len)
|
||||
.field("items", &self.iter())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SmlType<'a>> ListOf<'a, T> {
|
||||
pub fn iter(&self) -> ListIter<'a, T> {
|
||||
ListIter::new(self.data, self.len)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ListIter<'a, T: SmlType<'a>> {
|
||||
len: usize,
|
||||
cursor: ParserContext<'a>,
|
||||
index: usize,
|
||||
_t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: SmlType<'a>> Clone for ListIter<'a, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
len: self.len,
|
||||
cursor: self.cursor.clone(),
|
||||
index: self.index,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SmlType<'a> + Debug> Debug for ListIter<'a, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut list = f.debug_list();
|
||||
for i in self.clone().filter_map(|x| x.ok()) {
|
||||
list.entry(&i);
|
||||
}
|
||||
list.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: SmlType<'a>> ListIter<'a, T> {
|
||||
pub const fn new(data: &'a [u8], len: usize) -> Self {
|
||||
Self {
|
||||
len,
|
||||
cursor: ParserContext(data),
|
||||
index: 0,
|
||||
_t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a: 'b, 'b, T: SmlType<'a>> Iterator for ListIter<'a, T> {
|
||||
type Item = Result<T, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.index += 1;
|
||||
if self.index < self.len {
|
||||
Some(T::parse(&mut self.cursor).map_err(|x| {
|
||||
self.index = self.len;
|
||||
x
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SmlType<'a> for bool {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind != FieldType::Boolean {
|
||||
return Err(Error::ExpectedBool);
|
||||
}
|
||||
let val = buf.read(tlf.data_len())?;
|
||||
Ok(val.iter().any(|x| *x > 0))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_sml_type {
|
||||
($ident:ident, $type:expr, $len:expr) => {
|
||||
impl<'a> SmlType<'a> for $ident {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let tlf = TypeLengthField::parse(buf)?;
|
||||
if tlf.kind != $type && tlf.data_len() <= $len {
|
||||
return Err(Error::ExpectedUnsigned);
|
||||
}
|
||||
const SIZE: usize = $len;
|
||||
let mut val = [0; SIZE];
|
||||
val[SIZE - tlf.data_len()..].copy_from_slice(buf.read(tlf.data_len())?);
|
||||
Ok(Self::from_be_bytes(val))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_sml_type!(u8, FieldType::Unsigned, 1);
|
||||
impl_sml_type!(u16, FieldType::Unsigned, 2);
|
||||
impl_sml_type!(u32, FieldType::Unsigned, 4);
|
||||
impl_sml_type!(u64, FieldType::Unsigned, 8);
|
||||
|
||||
impl_sml_type!(i8, FieldType::Integer, 1);
|
||||
impl_sml_type!(i16, FieldType::Integer, 2);
|
||||
impl_sml_type!(i32, FieldType::Integer, 4);
|
||||
impl_sml_type!(i64, FieldType::Integer, 8);
|
||||
|
||||
impl<'a, T: SmlType<'a>> SmlType<'a> for Option<T> {
|
||||
fn parse(buf: &mut ParserContext<'a>) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if *buf.peek(0)? == 1 {
|
||||
buf.move_fwd(1)?;
|
||||
Ok(None)
|
||||
} else {
|
||||
T::parse(buf).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue