feat(ingest_protocol): improve parser
This commit is contained in:
parent
427a3095f8
commit
824e382e89
8 changed files with 124 additions and 84 deletions
|
@ -12,5 +12,8 @@ pub enum Error<I: std::fmt::Debug> {
|
||||||
TimestampParseError(ParseFloatError),
|
TimestampParseError(ParseFloatError),
|
||||||
|
|
||||||
#[error("Oops it blew up")]
|
#[error("Oops it blew up")]
|
||||||
UnknownUnit(I)
|
UnknownUnit(I),
|
||||||
|
|
||||||
|
#[error("Oops it blew up")]
|
||||||
|
DecimalParseError(#[from] rust_decimal::Error)
|
||||||
}
|
}
|
|
@ -3,5 +3,8 @@ pub mod error;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
pub use packet_types::*;
|
pub use packet_types::*;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rust_decimal::Decimal;
|
||||||
use crate::hashes::SupportedUnit;
|
use crate::hashes::SupportedUnit;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NarodMonValues<'a> {
|
pub struct SensorValue<'a> {
|
||||||
pub mac: Cow<'a, str>,
|
pub mac: Cow<'a, str>,
|
||||||
pub value: Decimal,
|
pub value: Decimal,
|
||||||
pub time: Option<Epoch>,
|
pub time: Option<Epoch>,
|
||||||
|
@ -14,19 +14,19 @@ pub struct NarodMonValues<'a> {
|
||||||
pub name: Option<Cow<'a, str>>,
|
pub name: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Hash for NarodMonValues<'a> {
|
impl<'a> Hash for SensorValue<'a> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.mac.hash(state);
|
self.mac.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PartialEq for NarodMonValues<'a> {
|
impl<'a> PartialEq for SensorValue<'a> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.mac == other.mac
|
self.mac == other.mac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Eq for NarodMonValues<'a> {
|
impl<'a> Eq for SensorValue<'a> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ impl<'a> Eq for NarodMonValues<'a> {
|
||||||
pub struct NarodMonPacket<'a> {
|
pub struct NarodMonPacket<'a> {
|
||||||
pub mac: [u8; 6],
|
pub mac: [u8; 6],
|
||||||
pub name: Option<Cow<'a, str>>,
|
pub name: Option<Cow<'a, str>>,
|
||||||
pub values: HashSet<NarodMonValues<'a>>,
|
pub values: HashSet<SensorValue<'a>>,
|
||||||
pub owner: Option<Cow<'a, str>>,
|
pub owner: Option<Cow<'a, str>>,
|
||||||
pub lat: Option<Decimal>,
|
pub lat: Option<Decimal>,
|
||||||
pub lon: Option<Decimal>,
|
pub lon: Option<Decimal>,
|
|
@ -10,6 +10,7 @@ use nom::bytes::complete::tag;
|
||||||
use nom::bytes::streaming::take_till;
|
use nom::bytes::streaming::take_till;
|
||||||
use nom::character::streaming::{anychar, char, hex_digit0, newline};
|
use nom::character::streaming::{anychar, char, hex_digit0, newline};
|
||||||
use nom::character::complete::hex_digit1;
|
use nom::character::complete::hex_digit1;
|
||||||
|
use nom::character::is_digit;
|
||||||
|
|
||||||
use nom::combinator::{map, map_opt, map_parser, opt, recognize, rest};
|
use nom::combinator::{map, map_opt, map_parser, opt, recognize, rest};
|
||||||
use nom::Err as NomErr;
|
use nom::Err as NomErr;
|
||||||
|
@ -17,9 +18,9 @@ use nom::error::{context, ContextError, ErrorKind as NomErrorKind, ParseError as
|
||||||
use nom::multi::{count, separated_list0, separated_list1};
|
use nom::multi::{count, separated_list0, separated_list1};
|
||||||
use nom::sequence::{delimited, preceded, separated_pair};
|
use nom::sequence::{delimited, preceded, separated_pair};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use crate::protocol::error::Error;
|
use crate::ingest_protocol::error::Error;
|
||||||
use crate::protocol::{NarodMonPacket, NarodMonValues};
|
use crate::ingest_protocol::{NarodMonPacket, SensorValue};
|
||||||
use crate::protocol::error::Error::TimestampParseError;
|
use crate::ingest_protocol::error::Error::TimestampParseError;
|
||||||
|
|
||||||
type MyIError<I, O> = Result<(I, O), Error<I>>;
|
type MyIError<I, O> = Result<(I, O), Error<I>>;
|
||||||
|
|
||||||
|
@ -32,7 +33,6 @@ pub fn parse_mac_address(input: &str) -> MyIError<&str, [u8; 6]> {
|
||||||
let (leftovers, i) = context("17 символов для MAC адреса", take(17usize))(input)?;
|
let (leftovers, i) = context("17 символов для MAC адреса", take(17usize))(input)?;
|
||||||
|
|
||||||
let (_, out) = count(|inp| {
|
let (_, out) = count(|inp| {
|
||||||
//dbg!(inp);
|
|
||||||
let (mut i, o) = context("Октет", map_parser(take(2usize), hex_digit1))(inp)?;
|
let (mut i, o) = context("Октет", map_parser(take(2usize), hex_digit1))(inp)?;
|
||||||
if counter != 5 {
|
if counter != 5 {
|
||||||
(i, _) = tag("-")(i)?;
|
(i, _) = tag("-")(i)?;
|
||||||
|
@ -41,65 +41,61 @@ pub fn parse_mac_address(input: &str) -> MyIError<&str, [u8; 6]> {
|
||||||
Ok((i, o))
|
Ok((i, o))
|
||||||
}, 6)(i)?;
|
}, 6)(i)?;
|
||||||
|
|
||||||
//dbg!(&out);
|
|
||||||
|
|
||||||
hex::decode_to_slice(out.join(""), &mut mac).unwrap();
|
hex::decode_to_slice(out.join(""), &mut mac).unwrap();
|
||||||
|
|
||||||
Ok((leftovers, mac))
|
Ok((leftovers, mac))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_packet(input: &str) -> MyIError<&str, NarodMonPacket> {
|
fn handle_special_sensor_macs<I: std::fmt::Debug> (
|
||||||
let (input, _) = tag("#")(input)?;
|
mac: &str,
|
||||||
|
sensor_value: &str,
|
||||||
|
packet: &mut NarodMonPacket,
|
||||||
|
) -> Result<bool, Error<I>> {
|
||||||
|
match mac.to_uppercase().as_str() {
|
||||||
|
"LAT" => packet.lat = Some(Decimal::from_str(sensor_value)?),
|
||||||
|
"LON" => packet.lon = Some(Decimal::from_str(sensor_value)?),
|
||||||
|
"ALT" => packet.alt = Some(Decimal::from_str(sensor_value)?),
|
||||||
|
_ => {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
let (input, mac) = parse_mac_address(input)?;
|
pub fn parse_packet_body<'a>(line: &'a str, packet: &mut NarodMonPacket<'a>) -> MyIError<&'a str, ()> {
|
||||||
|
let (line, _) = tag("#")(line)?;
|
||||||
|
let (line, sensor_mac) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
||||||
|
|
||||||
let (input, opt_name) = opt(delimited(tag("#"), take_while(|c| c != '\n'), tag("\n")))(input)?;
|
let (line, _) = tag("#")(line)?;
|
||||||
|
|
||||||
let mut packet = NarodMonPacket::default();
|
match sensor_mac {
|
||||||
packet.mac = mac;
|
"OWNER" => {
|
||||||
|
let (line, owner_value) = take_while1(|c| c != '\n')(line)?;
|
||||||
|
|
||||||
let mut hs = HashSet::new();
|
packet.owner = Some(owner_value.into())
|
||||||
|
|
||||||
let (input, asd) = context(
|
//hs.insert(NarodMonValues::Owner(owner_value.into()));
|
||||||
"Получение значений до тега терминатора",
|
//let (line, _) = tag("\n")(line)?;
|
||||||
map_parser(
|
}
|
||||||
take_until1("##"),
|
_ => {
|
||||||
separated_list0(tag("\n"), take_while1(|c| c != '\n'))
|
let (line, sensor_value) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
||||||
)
|
let (line, sensor_time) = opt(preceded(tag("#"), take_while1(|c| c != '\n' && c != '#' && "1234567890.".contains(c))))(line)?;
|
||||||
)(input)?;
|
let (line, sensor_name) = opt(preceded(tag("#"), take_while1(|c| c != '\n')))(line)?;
|
||||||
|
|
||||||
|
|
||||||
println!("ASDASD: {:?}", asd);
|
let sensor_time = match sensor_time {
|
||||||
|
Some(v) => Some(
|
||||||
|
Epoch::from_unix_seconds(
|
||||||
|
v.parse().map_err(|e| TimestampParseError(e))?
|
||||||
|
)
|
||||||
|
),
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
|
||||||
for line in asd {
|
if !handle_special_sensor_macs(sensor_mac, sensor_value, packet)? {
|
||||||
let (line, _) = tag("#")(line)?;
|
packet.values.insert(SensorValue {
|
||||||
let (line, sensor_mac) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
|
||||||
|
|
||||||
let (line, _) = tag("#")(line)?;
|
|
||||||
|
|
||||||
match sensor_mac {
|
|
||||||
"OWNER" => {
|
|
||||||
let (line, owner_value) = take_while1(|c| c != '\n')(line)?;
|
|
||||||
|
|
||||||
packet.owner = Some(owner_value.into())
|
|
||||||
|
|
||||||
//hs.insert(NarodMonValues::Owner(owner_value.into()));
|
|
||||||
//let (line, _) = tag("\n")(line)?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let (line, sensor_value) = take_while1(|c| c != '\n' && c != '#')(line)?;
|
|
||||||
let (line, sensor_time) = opt(preceded(tag("#"), take_while1(|c| c != '\n' && c != '#')))(line)?;
|
|
||||||
let (line, sensor_name) = opt(preceded(tag("#"), take_while1(|c| c != '\n')))(line)?;
|
|
||||||
|
|
||||||
|
|
||||||
let sensor_time = match sensor_time {
|
|
||||||
Some(v) => Some(Epoch::from_unix_seconds(v.parse().map_err(|e| TimestampParseError(e))?)),
|
|
||||||
None => None
|
|
||||||
};
|
|
||||||
|
|
||||||
hs.insert(NarodMonValues {
|
|
||||||
mac: sensor_mac.into(),
|
mac: sensor_mac.into(),
|
||||||
value: Decimal::from_str(sensor_value).unwrap(),
|
value: Decimal::from_str(sensor_value)?,
|
||||||
time: sensor_time, // TODO
|
time: sensor_time, // TODO
|
||||||
unit: None,
|
unit: None,
|
||||||
name: sensor_name.map(|v| Cow::from(v)),
|
name: sensor_name.map(|v| Cow::from(v)),
|
||||||
|
@ -108,10 +104,34 @@ pub fn parse_packet(input: &str) -> MyIError<&str, NarodMonPacket> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Ok((line, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_packet(input: &str) -> MyIError<&str, NarodMonPacket> {
|
||||||
|
let (input, _) = tag("#")(input)?;
|
||||||
|
|
||||||
|
let (input, device_mac) = parse_mac_address(input)?;
|
||||||
|
|
||||||
|
let (input, opt_name) = opt(delimited(tag("#"), take_while(|c| c != '\n'), tag("\n")))(input)?;
|
||||||
|
|
||||||
|
let mut packet = NarodMonPacket::default();
|
||||||
|
packet.mac = device_mac;
|
||||||
|
|
||||||
|
let (input, lines) = context(
|
||||||
|
"Получение значений до тега терминатора",
|
||||||
|
map_parser(
|
||||||
|
take_until1("##"),
|
||||||
|
separated_list0(tag("\n"), take_while1(|c| c != '\n'))
|
||||||
|
)
|
||||||
|
)(input)?;
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
parse_packet_body(line, &mut packet)?;
|
||||||
|
}
|
||||||
|
|
||||||
let (input, _) = tag("##")(input)?;
|
let (input, _) = tag("##")(input)?;
|
||||||
|
|
||||||
packet.name = opt_name.map(|v| Cow::from(v));
|
packet.name = opt_name.map(|v| Cow::from(v));
|
||||||
packet.values = hs;
|
|
||||||
|
|
||||||
Ok((input, packet))
|
Ok((input, packet))
|
||||||
}
|
}
|
37
src/ingest_protocol/tests/mod.rs
Normal file
37
src/ingest_protocol/tests/mod.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::ingest_protocol::parser::{parse_mac_address, parse_packet};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_asd() {
|
||||||
|
let asd = r#"
|
||||||
|
#A2-C6-47-01-DF-E1#Метео
|
||||||
|
#OWNER#unknown
|
||||||
|
#T1#13.44#Outdoor
|
||||||
|
#T2#27.74#Indoor
|
||||||
|
#P1#691.02#Barometer
|
||||||
|
#LAT#55.738178
|
||||||
|
#LON#37.6068
|
||||||
|
#ALT#38
|
||||||
|
##
|
||||||
|
"#.trim();
|
||||||
|
dbg!(parse_packet(asd));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mac() {
|
||||||
|
//assert_eq!(parse_mac_address("12-34-AA-12-55-AA"), Ok(("", [18, 52, 170, 18, 85, 170])) );
|
||||||
|
|
||||||
|
println!("{:?}", parse_mac_address("12-34-AA-12-55-AA"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packet() {
|
||||||
|
let inp = r#"#26-94-1D-75-C2-F8#Метео
|
||||||
|
#OWNER#nm17
|
||||||
|
#T1#1.12
|
||||||
|
#T2#10000#1231410321#sensorName
|
||||||
|
#T2#1.2#3400005345
|
||||||
|
##"#;
|
||||||
|
|
||||||
|
|
||||||
|
println!("{:#?}", parse_packet(inp));
|
||||||
|
}
|
32
src/main.rs
32
src/main.rs
|
@ -38,7 +38,7 @@ extern crate core;
|
||||||
|
|
||||||
mod hashes;
|
mod hashes;
|
||||||
mod web_server;
|
mod web_server;
|
||||||
mod protocol;
|
mod ingest_protocol;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||||
|
@ -50,9 +50,9 @@ use axum::routing::post;
|
||||||
use hifitime::Epoch;
|
use hifitime::Epoch;
|
||||||
|
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use crate::protocol::{NarodMonPacket, NarodMonValues};
|
use crate::ingest_protocol::{NarodMonPacket, SensorValue};
|
||||||
use crate::protocol::error::Error;
|
use crate::ingest_protocol::error::Error;
|
||||||
use crate::protocol::error::Error::TimestampParseError;
|
use crate::ingest_protocol::error::Error::TimestampParseError;
|
||||||
use crate::web_server::old_app_api::old_api_handler;
|
use crate::web_server::old_app_api::old_api_handler;
|
||||||
use crate::web_server::server_main;
|
use crate::web_server::server_main;
|
||||||
|
|
||||||
|
@ -84,28 +84,4 @@ async fn main() {
|
||||||
web_server_hndl.await.unwrap();
|
web_server_hndl.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::protocol::parser::{parse_mac_address, parse_packet};
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mac() {
|
|
||||||
//assert_eq!(parse_mac_address("12-34-AA-12-55-AA"), Ok(("", [18, 52, 170, 18, 85, 170])) );
|
|
||||||
|
|
||||||
println!("{:?}", parse_mac_address("12-34-AA-12-55-AA"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_packet() {
|
|
||||||
let inp = r#"#26-94-1D-75-C2-F8#Метео
|
|
||||||
#OWNER#nm17
|
|
||||||
#T1#1.12
|
|
||||||
#T2#10000#1231410321#sensorName
|
|
||||||
#T2#1.2#3400005345
|
|
||||||
##"#;
|
|
||||||
|
|
||||||
|
|
||||||
println!("{:#?}", parse_packet(inp));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct AppInitRequest<'a> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct AddLikeRequest {
|
pub struct AddLikeRequest {
|
||||||
|
/// TODO: WTF is this? i dont remember
|
||||||
pub version: u64,
|
pub version: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue