1use nom::{
2 branch::{alt,permutation}, bytes::complete::tag, character::complete::{alpha1, alphanumeric1, digit1, multispace0}, combinator::{map, opt, peek, recognize}, error::Error, multi::separated_list1 as separated_list, number::complete::double, sequence::{pair, preceded, terminated}, Err, IResult, InputTake, Needed
3};
4
5use chrono::{NaiveDateTime,NaiveDate};
6use record::{B2BDetails, EndOfData, Header, IntervalData, IntervalEvent, NMIDataDetails};
7use std::{str, sync::Mutex};
8
9use crate::common::*;
10
11#[derive(Clone,Debug,PartialEq)]
12pub struct NEM12<'a> {
13 header: record::Header<'a>,
14 nmi_data_details: Vec<record::NMIDataDetails<'a>>
15}
16
17fn parse_nmi_data_details<'a>(input:Input<'a>) -> IResult<Input,NMIDataDetails> {
18 let (input, mut nmi_details) = terminated(NMIDataDetails::parse,rec_separator)(input)?;
19 let interval_data_len = 1440 / nmi_details.interval_length;
20 let (input_pre_b2b,interval_data) = opt(separated_list(rec_separator, parse_interval_data(interval_data_len)))(input)?;
21 let (input,_) = rec_separator(input_pre_b2b)?;
22
23 nmi_details.interval_data_vec = interval_data;
24
25 if let (input,Some(b2b_details)) = opt(separated_list(rec_separator, B2BDetails::parse))(input)? {
26 nmi_details.b2b_details = Some(b2b_details);
27 Ok((input,nmi_details))
28 } else {
29 Ok((input_pre_b2b,nmi_details))
30 }
31
32}
33
34fn parse_interval_data<'a>(capacity: usize) -> impl FnMut(Input<'a>) -> IResult<Input<'a>,IntervalData<'a>> {
35 move |input: Input<'a>| {
36 let (input_before_events, mut interval_data) = IntervalData::parse(capacity,input)?;
37 let (input, _) = rec_separator(input_before_events)?;
38 let (input, interval_events) = opt(separated_list(rec_separator, IntervalEvent::parse))(input)?;
39 interval_data.interval_events = interval_events;
40
41 if interval_data.interval_events.is_some() {
42 Ok((input,interval_data))
43 } else {
44 Ok((input_before_events,interval_data))
45 }
46 }
47}
48
49impl <'a>NEM12<'a> {
50 fn new(header: Header<'a>, nmi_data_details: Vec<NMIDataDetails<'a>>) -> Self {
51 NEM12 {
52 header,
53 nmi_data_details
54 }
55 }
56
57 fn from_str(input: Input<'a>) -> Result<NEM12<'a>,Err<Error<Input<'a>>>> {
58 let strict = true;
59 let (input,header) = Header::parse(input)?;
60 let (input,_) = rec_separator(input)?;
61 let (input,nmi_data_details) = separated_list(rec_separator, parse_nmi_data_details)(input)?;
62 let (input,_) = rec_separator(input)?;
63 let (_input,_) = EndOfData::parse(input)?;
64
65 Ok(NEM12 {
66 header,
67 nmi_data_details
68 })
69 }
70}
71
72struct Parser<'a> {
73 src: Input<'a>,
74 iter: std::iter::Peekable<std::iter::Enumerate<str::Lines<'a>>>,
75 data: Option<NEM12<'a>>,
76 finished: bool,
77 ctx: Mutex<(usize,)>,
78 }
80
81impl <'a>Parser<'a> {
82
83 fn new(src: Input<'a>) -> Self {
84 let parser = Parser {
85 src,
86 iter: src.lines().enumerate().peekable(),
87 data: None,
88 finished: false,
89 ctx:Mutex::new((0,)),
90 };
92
93 parser
94 }
95
96 fn parse_line(&mut self) -> Result<Option<record::Kind>,(&'static str,usize,Err<Error<Input<'a>>>)> {
97 if let Some((line_no,line)) = self.iter.next() {
98 let res = alt((
100 map(record::Header::parse, |o| Some(record::Kind::Header(o))),
101 map(record::NMIDataDetails::parse, |o| {
102 let mut mutex = self.ctx.lock().unwrap();
103 mutex.0 = 1440usize / o.interval_length;
104 Some(record::Kind::NMIDataDetails(o))
105 }),
106 map(|input|record::IntervalData::parse(self.ctx.lock().unwrap().0,input),
107 |o| { Some(record::Kind::IntervalData(o)) }),
108 map(record::IntervalEvent::parse, |o| Some(record::Kind::IntervalEvent(o))),
109 map(record::B2BDetails::parse, |o| Some(record::Kind::B2BDetails(o))),
110 map(record::EndOfData::parse, |o| Some(record::Kind::EndOfData(o))),
111 ))(line.into())
112 .map_err(|err| {
113 match err {
114 Err::Error(sentinel) => ("Error parsing line: ",line_no+1,Err::Error(sentinel)),
115 Err::Failure(sentinel) => ("Failed to parse line: ",line_no+1,Err::Failure(sentinel)),
116 Err::Incomplete(needed) => match needed {
117 Needed::Size(sz) => ("Failed to parse line: ",sz.into(),err),
118 Needed::Unknown => ("Failed to parse line: ",line_no+1,err)
119 }
120 }
121 });
122 match res {
123 Ok(r) => Ok(r.1),
124 Err(e) => Err(e),
125 }
126
127 } else {
129 match !self.finished {
130 true => {
131 self.finished = true;
132 Ok(None)
133 }
134 false => Err(("Error: parser consumed all input",0,nom::Err::Incomplete(Needed::Unknown)))
135 }
136 }
137 }
138}
139
140pub mod file {
141 use super::*;
142
143 #[cfg(test)]
144 mod tests {
145 use super::*;
146 use pretty_assertions::{assert_eq};
147
148 const MULTIPLE_METERS_STR: &'static str = "100,NEM12,200402070911,MDA1,Ret1\n\
149 200,NCDE001111,E1B1Q1E2,1,E1,N1,METSER123,Wh,15,\n\
150 300,20031204,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
151 300,20031205,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
152 200,NCDE001111,E1B1Q1E2,2,B1,N1,METSER123,Wh,15,\n\
153 300,20031204,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
154 300,20031205,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
155 200,NCDE001111,E1B1Q1E2,3,Q1,,METSER123,VArh,15,\n\
156 300,20031204,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,A,,,20031206011155,\n\
157 300,20031205,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,A,,,20031206011155,\n\
158 200,NCDE001111,E1B1Q1E2,4,E2,N2,METSER456,Wh,15,\n\
159 300,20031204,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,A,,,20031206011140,20031207011022\n\
160 300,20031205,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,A,,,20031206011140,20031207011022\n\
161 200,NDDD001888,B1K2,1,B1,N1,METSER991,Wh,15,\n\
162 300,20031204,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,A,,,20031206011145,20031207011022\n\
163 300,20031205,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,A,,,20031206011145,20031207011022\n\
164 200,NDDD001888,B1K2,2,K2,,METSER992,VArh,15,\n\
165 300,20031204,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,A,,,20031206011155,\n\
166 300,20031205,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,A,,,20031206011155,\n\
167 900";
168
169 const DATADETAILS_ROWS_STR: &'static str = "200,NCDE001111,E1B1Q1E2,1,E1,N1,METSER123,Wh,15,\n\
170 300,20031204,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
171 300,20031205,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,A,,,20031206011132,20031207011022\n\
172 ";
173
174 const NEM12_WITH_QUALITY: &'static str = "100,NEM12,200404201300,MDA1,Ret1\n\
175 200,CCCC123456,E1,001,E1,N1,METSER123,kWh,30,\n\
176 300,20040417,18.023,19.150,17.592,24.155,18.568,22.304,19.222,19.032,19.090,22.237,24.350,22.274,20.193,16.615,19.575,20.391,16.459,20.527,21.438,19.327,21.424,16.656,17.616,18.416,16.666,19.961,18.120,18.023,18.588,21.759,17.841,19.548,18.486,21.391,15.656,16.634,16.377,14.246,17.451,15.742,18.038,18.470,14.936,17.987,15.751,19.750,16.202,14.733,V,,,20040418203500,20040419003500\n\
177 400,1,20,F14,76,\n\
178 400,21,24,A,,\n\
179 400,25,48,S14,1,\n\
180 900\n\
181 ";
182
183 #[test]
184 fn get_nmi_data_details_parser() {
185 let (input,nmi_data_details) = record::NMIDataDetails::parse(DATADETAILS_ROWS_STR.into()).unwrap();
186
187 let capacity = 1440 / nmi_data_details.interval_length;
188 let (input,_) = preceded(rec_separator,|i|{record::IntervalData::parse(capacity, i)})(input).unwrap();
189 let (input,_) = preceded(rec_separator,|i|{record::IntervalData::parse(capacity, i)})(input).unwrap();
190 assert_eq!(input.into_fragment(),"\n");
191
192 let (input, nmi_data_details) = parse_nmi_data_details(DATADETAILS_ROWS_STR.into()).unwrap();
193 }
195
196 #[test]
197 fn get_nmi_with_data_quality() {
198 let nmi_data_details = record::NMIDataDetails {
199 nmi: "CCCC123456".into(),
200 nmi_configuration: "E1".into(),
201 register_id: "001".into(),
202 nmi_suffix: "E1".into(),
203 mdm_data_stream_id: Some("N1".into()),
204 meter_serial_number: "METSER123".into(),
205 uom: "KWH".into(),
206 interval_length: 30usize,
207 next_scheduled_read_date: None,
208 interval_data_vec: Some(vec![IntervalData {
209 interval_date: NaiveDate::from_ymd(2004,04,17),
210 interval_value: vec![18.023, 19.15, 17.592, 24.155, 18.568, 22.304, 19.222, 19.032, 19.09, 22.237, 24.35, 22.274, 20.193, 16.615, 19.575, 20.391, 16.459, 20.527, 21.438, 19.327, 21.424, 16.656, 17.616, 18.416, 16.666, 19.961, 18.12, 18.023, 18.588, 21.759, 17.841, 19.548, 18.486, 21.391, 15.656, 16.634, 16.377, 14.246, 17.451, 15.742, 18.038, 18.47, 14.936, 17.987, 15.751, 19.75, 16.202, 14.733],
211 quality_method: "V".into(),
212 reason_code: None,
213 reason_description: None,
214 update_datetime: NaiveDateTime::parse_from_str("2004-04-18T20:35:00","%Y-%m-%dT%H:%M:%S").unwrap(),
215 msats_load_datetime: Some(NaiveDateTime::parse_from_str("2004-04-19T00:35:00","%Y-%m-%dT%H:%M:%S").unwrap()),
216 interval_events: Some(vec![
217 IntervalEvent::parse("400,1,20,F14,76,\n".into()).map(|(_,o)|o).unwrap(),
218 IntervalEvent::parse("400,21,24,A,,\n".into()).map(|(_,o)|o).unwrap(),
219 IntervalEvent::parse("400,25,48,S14,1,\n".into()).map(|(_,o)|o).unwrap(),
220 ])
221 }]),
222 b2b_details: None,
223 };
224 let nem12_test = NEM12 {
225 header: record::Header::new(
226 "NEM12".into(),
227 NaiveDate::from_ymd(2004,04,20).and_hms(13,0,0),
228 "MDA1".into(),
229 "Ret1".into()
230 ),
231 nmi_data_details: vec![nmi_data_details],
232 };
233 let nem12_obj = NEM12::from_str(NEM12_WITH_QUALITY.into());
234 assert_eq!(Ok(nem12_test),nem12_obj)
235 }
236
237 #[test]
238 fn multiple_meters_from_str() {
239 let _nem12_obj = NEM12::from_str(MULTIPLE_METERS_STR.into()).unwrap();
240 }
241
242 #[test]
243 fn multiple_meters() {
244
245 let mut parser = Parser::new(MULTIPLE_METERS_STR.into());
246
247 for _ in 1..20 {
248 parser.parse_line().unwrap();
249 }
250
251 assert_eq!(parser.parse_line(),Ok(Some(record::Kind::EndOfData(record::EndOfData{}))));
252 assert_eq!(parser.parse_line(),Ok(None));
253 assert_eq!(parser.parse_line(),Err(("Error: parser consumed all input",0,nom::Err::Incomplete(Needed::Unknown))));
254 }
255
256 #[test]
257 fn nem12_from_str() {
258 let _nem12_obj = NEM12::from_str(MULTIPLE_METERS_STR.into());
259 if let Err(e) = _nem12_obj {
260 println!("{:?}",e);
261 }
262 }
264 }
265}
266
267pub mod record {
268 use nom::multi::many_m_n;
269
270 use super::*;
271
272 #[derive(Clone,Debug,PartialEq)]
273 pub enum Kind<'a> {
274 Header(Header<'a>),
275 NMIDataDetails(NMIDataDetails<'a>),
276 IntervalData(IntervalData<'a>),
277 IntervalEvent(IntervalEvent<'a>),
278 B2BDetails(B2BDetails<'a>),
279 EndOfData(EndOfData)
280 }
281
282 #[derive(Clone,Debug)]
284 pub struct Header<'a> {
285 format: Input<'a>,
286 created: NaiveDateTime,
287 from_participant: Input<'a>,
288 to_participant: Input<'a>,
289 }
290
291 impl <'a>PartialEq for Header<'a> {
292 fn eq(&self, other: &Self) -> bool {
293 self.format.into_fragment() == other.format.into_fragment() &&
294 self.created == other.created &&
295 self.from_participant.into_fragment() == other.from_participant.into_fragment() &&
296 self.to_participant.into_fragment() == other.to_participant.into_fragment()
297 }
298 }
299
300 impl <'a>Eq for Header<'a> { }
301
302 impl <'a>Header<'a> {
303 pub fn new(format:Input<'a>, created: NaiveDateTime, from_participant: Input<'a>, to_participant: Input<'a>) -> Self {
304 Header {
305 format,
306 created,
307 from_participant,
308 to_participant
309 }
310 }
311
312 pub fn parse(input: Input) -> IResult<Input,Header> {
313 let (input, _) = tag("100,")(input)?;
314 let (input, format) = alt((tag("NEM12"),tag("NEM13")))(input)?;
315 let (input, _) = tag(",")(input)?;
316 let (input, created) = datetime_12(input)?;
317 let (input, _) = tag(",")(input)?;
318 let (input, from_participant) = section_of_max_length(alphanumeric1,10)(input)?;
319 let (input, _) = tag(",")(input)?;
320 let (input, to_participant) = section_of_max_length(alphanumeric1,10)(input)?;
321
322 let header = Header::new(
323 format,
324 created,
325 from_participant,
326 to_participant
327 );
328
329 Ok((input,header))
330 }
331 }
332
333 #[derive(Clone,Debug)]
335 pub struct NMIDataDetails<'a> {
336 pub nmi: Input<'a>,
337 pub nmi_configuration: Input<'a>,
338 pub register_id: Input<'a>,
339 pub nmi_suffix: Input<'a>,
340 pub mdm_data_stream_id: Option<Input<'a>>,
341 pub meter_serial_number: Input<'a>,
342 pub uom: Input<'a>,
343 pub interval_length: usize,
344 pub next_scheduled_read_date: Option<NaiveDate>,
345 pub interval_data_vec: Option<Vec<IntervalData<'a>>>,
346 pub b2b_details: Option<Vec<B2BDetails<'a>>>,
347 }
348
349 impl <'a>PartialEq for NMIDataDetails<'a> {
350 fn eq(&self, other: &Self) -> bool {
351 self.nmi.into_fragment() == other.nmi.into_fragment() &&
352 self.nmi_configuration.into_fragment() == other.nmi_configuration.into_fragment() &&
353 self.register_id.into_fragment() == other.register_id.into_fragment() &&
354 self.nmi_suffix.into_fragment() == other.nmi_suffix.into_fragment() &&
355 self.mdm_data_stream_id.map(|o| o.into_fragment()) == other.mdm_data_stream_id.map(|o| o.into_fragment()) &&
356 self.meter_serial_number.into_fragment() == other.meter_serial_number.into_fragment() &&
357 self.uom.into_fragment().to_owned().to_lowercase() == other.uom.into_fragment().to_owned().to_lowercase() &&
358 self.interval_length == other.interval_length &&
359 self.next_scheduled_read_date == other.next_scheduled_read_date &&
360 self.interval_data_vec == other.interval_data_vec &&
361 self.b2b_details == other.b2b_details
362 }
363 }
364
365 impl <'a>Eq for NMIDataDetails<'a> { }
366
367 impl <'a>NMIDataDetails<'a> {
368 pub fn parse(input: Input) -> IResult<Input,NMIDataDetails> {
369 let (input, _) = tag("200,")(input)?;
370 let (input, nmi) = section_of_exact_length(alphanumeric1, 10)(input)?;
371 let (input, _) = tag(",")(input)?;
372 let (input, nmi_configuration) = section_of_max_length(alphanumeric1, 240)(input)?;
373 let (input, _) = tag(",")(input)?;
374 let (input, register_id) = section_of_max_length(alphanumeric1, 10)(input)?;
375 let (input, _) = tag(",")(input)?;
376 let (input, nmi_suffix) = section_of_exact_length(alphanumeric1, 2)(input)?;
377 let (input, _) = tag(",")(input)?;
378 let (input, mdm_data_stream_id) = optional_field(section_of_exact_length(alphanumeric1, 2),",")(input)?;
379 let (input, _) = tag(",")(input)?;
380 let (input, meter_serial_number) = section_of_max_length(alphanumeric1, 12)(input)?;
381 let (input, _) = tag(",")(input)?;
382 let (input, uom) = section_of_max_length(alphanumeric1, 5)(input)?;
383 let (input, _) = tag(",")(input)?;
384 let (input, interval_length) = section_of_exact_length(digit1, 2)(input).map(|(input,val)| (input,val.parse::<usize>().unwrap()))?;
385 let (input, _) = tag(",")(input)?;
386 let (input, next_scheduled_read_date) = match date_8(input){
387 Ok(d) => Ok((d.0,Some(d.1))),
388 Err(nom::Err::Error(_)) => {
389 match peek(alt((eof,tag("\n"))))(input) { Ok((input,_)) => Ok((input,None)),
391 Err(nom::Err::Error(e)) => {
392 return Err(nom::Err::Error(e))
393 }
394 x => { println!("'{:?}'", x); panic!("This should never happen") }
395 }
396 },
397 x => { println!("{:?}", x); panic!("This should never happen") }
398 }?;
399
400 let nmi_data_details = NMIDataDetails {
407 nmi,
408 nmi_configuration,
409 register_id,
410 nmi_suffix,
411 mdm_data_stream_id,
412 meter_serial_number,
413 uom,
414 interval_length,
415 next_scheduled_read_date,
416 interval_data_vec: None,
417 b2b_details: None,
418 };
419
420 Ok((input,nmi_data_details))
421 }
422 }
423
424 #[derive(Clone,Debug)]
426 pub struct IntervalData<'a> {
427 pub interval_date: NaiveDate,
428 pub interval_value: Vec<f64>,
429 pub quality_method: Input<'a>,
430 pub reason_code: Option<Input<'a>>,
431 pub reason_description: Option<Input<'a>>,
432 pub update_datetime: NaiveDateTime,
433 pub msats_load_datetime: Option<NaiveDateTime>,
434 pub interval_events: Option<Vec<IntervalEvent<'a>>>
435 }
436
437 impl <'a>PartialEq for IntervalData<'a> {
438 fn eq(&self, other: &Self) -> bool {
439 self.interval_date == other.interval_date &&
440 self.interval_value == other.interval_value &&
441 self.quality_method.into_fragment() == other.quality_method.into_fragment() &&
442 self.reason_code.map(|o| o.into_fragment()) == other.reason_code.map(|o| o.into_fragment()) &&
443 self.reason_description.map(|o| o.into_fragment()) == other.reason_description.map(|o| o.into_fragment()) &&
444 self.update_datetime == other.update_datetime &&
445 self.msats_load_datetime == other.msats_load_datetime &&
446 self.interval_events == other.interval_events
447 }
448 }
449
450 impl <'a>Eq for IntervalData<'a> { }
451
452 impl <'a>IntervalData<'a> {
453 pub fn parse(capacity: usize, input: Input<'a>) -> IResult<Input<'a>,IntervalData<'a>> {
454 interval_data(capacity, input)
455 }
456 }
457
458 fn interval_data<'a>(capacity: usize, input: Input<'a>) -> IResult<Input<'a>,IntervalData<'a>> {
459 let (input, _) = tag("300,")(input)?;
460 let (input, interval_date) = date_8(input)?;
461 let (input, interval_value) = many_m_n(capacity,capacity,preceded(tag(","),double))(input)?;
462
463 let (input, _) = tag(",")(input)?;
470 let (input, quality_method) = section_of_max_length(alpha1, 3)(input)?;
471 let (input, _) = tag(",")(input)?;
472 let (input, reason_code) = optional_field(section_of_max_length(digit1, 3),",")(input)?;
473
474 let (input, _) = tag(",")(input)?;
475 let (input, reason_description) = optional_field(section_of_max_length(alphanumeric1, 240),",")(input)?;
476
477 let (input, _) = tag(",")(input)?;
478 let (input, update_datetime) = datetime_14(input)?;
479 let (input, _) = tag(",")(input)?;
480 let (input, msats_load_datetime) = opt(datetime_14)(input)?;
481
482 let interval_data = IntervalData {
487 interval_date,
488 interval_value,
489 quality_method,
490 reason_code,
491 reason_description,
492 update_datetime,
493 msats_load_datetime,
494 interval_events: None
495 };
496
497 Ok((input,interval_data))
498 }
499
500 #[derive(Clone,Debug)]
502 pub struct IntervalEvent<'a> {
503 pub start_interval: Input<'a>,
504 pub end_interval: Input<'a>,
505 pub quality_method: Input<'a>,
506 pub reason_code: Option<Input<'a>>,
507 pub reason_description: Option<Input<'a>>,
508 }
509
510 impl <'a>PartialEq for IntervalEvent<'a> {
511 fn eq(&self, other: &Self) -> bool {
512 self.start_interval.into_fragment() == other.start_interval.into_fragment() &&
513 self.end_interval.into_fragment() == other.end_interval.into_fragment() &&
514 self.quality_method.into_fragment() == other.quality_method.into_fragment() &&
515 self.reason_code.map(|o| o.into_fragment()) == other.reason_code.map(|o| o.into_fragment()) &&
516 self.reason_description.map(|o| o.into_fragment()) == other.reason_description.map(|o| o.into_fragment())
517 }
518 }
519
520 impl <'a>Eq for IntervalEvent<'a> { }
521
522 impl <'a>IntervalEvent<'a> {
523 pub fn parse(input: Input<'a>) -> IResult<Input<'a>,IntervalEvent> {
524 interval_event(input)
525 }
526 }
527
528 fn interval_event<'a>(input: Input<'a>) -> IResult<Input<'a>,IntervalEvent<'a>> {
529 let (input, _) = tag("400,")(input)?;
530 let (input, start_interval) = section_of_max_length(digit1,4)(input)?;
531 let (input, _) = tag(",")(input)?;
532 let (input, end_interval) = section_of_max_length(digit1,4)(input)?;
533 let (input, _) = tag(",")(input)?;
534 let (input, quality_method) = section_of_max_length(alphanumeric1,3)(input)?;
535 let (input, _) = tag(",")(input)?;
536 let (input, reason_code) = optional_field(section_of_max_length(digit1,3),",")(input)?;
537 let (input, _) = tag(",")(input)?;
538 let (input, reason_description) = optional_field(section_of_max_length(alphanumeric1,24),"\n")(input)?;
539
540 let interval_event = IntervalEvent {
541 start_interval,
542 end_interval,
543 quality_method,
544 reason_code,
545 reason_description
546 };
547
548 Ok((input,interval_event))
549 }
550
551 #[derive(Clone,Debug)]
553 pub struct B2BDetails<'a> {
554 pub trans_code: Input<'a>,
555 pub ret_service_order: Input<'a>,
556 pub read_datetime: NaiveDateTime,
557 pub index_read: Input<'a>,
558 }
559
560 impl <'a>PartialEq for B2BDetails<'a> {
561 fn eq(&self, other: &Self) -> bool {
562 self.trans_code.into_fragment() == other.trans_code.into_fragment() &&
563 self.ret_service_order.into_fragment() == other.ret_service_order.into_fragment() &&
564 self.read_datetime == other.read_datetime &&
565 self.index_read.into_fragment() == other.index_read.into_fragment()
566 }
567 }
568
569 impl <'a>Eq for B2BDetails<'a> { }
570
571 impl B2BDetails<'_> {
572 pub fn parse(input: Input) -> IResult<Input,B2BDetails> {
573 b2b_details(input)
574 }
575 }
576
577 fn b2b_details<'a>(input: Input<'a>) -> IResult<Input<'a>,B2BDetails<'a>> {
578 let (input, _) = tag("500,")(input)?;
579 let (input, trans_code) = section_of_exact_length(alpha1,1)(input)?;
580 let (input, _) = tag(",")(input)?;
581 let (input, ret_service_order) = section_of_max_length(alphanumeric1,15)(input)?;
582 let (input, _) = tag(",")(input)?;
583 let (input, read_datetime) = datetime_14(input)?;
584 let (input, _) = tag(",")(input)?;
585 let (input, index_read) = section_of_max_length(
586 move |i| recognize(permutation((digit1,opt(pair(tag("."),digit1)))))(i)
587 ,15)(input)?;
588
589 let b2b_details = B2BDetails {
590 trans_code,
591 ret_service_order,
592 read_datetime,
593 index_read
594 };
595
596 Ok((input,b2b_details))
597 }
598
599 #[derive(Clone,Debug,PartialEq)]
601 pub struct EndOfData {}
602
603 impl EndOfData {
604 pub fn parse(input: Input) -> IResult<Input,EndOfData> {
605 end_of_data(input)
606 }
607 }
608
609 fn end_of_data(input: Input) -> IResult<Input,EndOfData> {
610 let (input, _) = tag("900")(input)?;
611 let (input, _) = multispace0(input)?; Ok((input,EndOfData {}))
613 }
614
615 #[cfg(test)]
616 mod tests {
617 use std::borrow::Borrow;
618
619 use super::{record,Input};
620 use nom::error;
621 use chrono::{NaiveDate};
622
623 #[test]
624 fn header_100() {
625 let date = NaiveDate::from_ymd(2004,5,1).and_hms(11, 35, 0);
626 let header = record::Header::new (
627 "NEM12".into(),
628 date.clone(),
629 "MDA1".into(),
630 "Ret1".into()
631 );
632
633 let raw = "100,NEM12,200405011135,MDA1,Ret1\n";
634
635 let res = record::Header::parse(raw.into());
636
637 assert_eq!(res.map(|(r,v)| (r.into_fragment(),v)),
638 Ok(("\n",header))
639 );
640
641 let header = record::Header::new (
642 "NEM12".into(),
643 date.clone().into(),
644 "0123456789".into(),
645 "Ret1".into()
646 );
647
648 let raw = "100,NEM12,200405011135,0123456789,Ret1\n";
649
650 let res = record::Header::parse(raw.into()); assert_eq!(res.map(|(r,v)| (r.into_fragment(),v)),
656 Ok(("\n",header))
657 );
658
659 let raw = "100,NEM12,200405011135,12345678910,Ret1\n";
660
661 let res = match record::Header::parse(raw.into())
662 .map(|(r,v)| (r.into_fragment(),v)) {
663 Ok(o) => { println!("{:?}",o); panic!("Failed") },
664 Err(nom::Err::Error(e)) => { (e.input.into_fragment(),error::ErrorKind::Verify) }, Err(nom::Err::Incomplete(_)) |
666 Err(nom::Err::Failure(_)) => panic!("This should never happen")
667 };
668
669 assert_eq!(res, ("12345678910,Ret1\n",error::ErrorKind::Verify));
670 }
671
672 #[test]
673 fn nmi_data_details_200() {
674 let nmi_data_details = record::NMIDataDetails {
675 nmi: "VABD000163".into(),
676 nmi_configuration: "E1Q1".into(),
677 register_id: "1".into(),
678 nmi_suffix: "E1".into(),
679 mdm_data_stream_id: Some("N1".into()),
680 meter_serial_number: "METSER123".into(),
681 uom: "KWH".into(),
682 interval_length: 30usize,
683 next_scheduled_read_date: None,
684 interval_data_vec: None,
685 b2b_details: None,
686 };
687
688 let raw = "200,VABD000163,E1Q1,1,E1,N1,METSER123,KWH,30,\n";
689
690 let res = record::NMIDataDetails::parse(raw.into());
691
692 assert_eq!(res.map(|(i,v)|(i.into_fragment(),v)),Ok(("\n",nmi_data_details)));
693
694 let raw = "200,VABD000163,E1Q1,1,E1,N1,METSER123,kWh,30,1234\n";
695
696 let res = record::NMIDataDetails::parse(raw.into());
697
698 assert_eq!(res.map(|(i,v)|(i.into_fragment(),v)).map_err(|e| {
699 match e {
700 nom::Err::Incomplete(e)=> nom::Err::Incomplete(e),
701 nom::Err::Error(e) => nom::Err::Error(e.input.into_fragment()),
702 nom::Err::Failure(e) => nom::Err::Failure(e.input.into_fragment()),
703 }
704 }),Err(nom::Err::Error("1234\n")));
705 }
706
707 #[test]
708 fn interval_data_300() {
709 let interval_data = record::IntervalData {
710 interval_date: NaiveDate::from_ymd(2004, 2, 1),
711 interval_value: vec![1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111],
712 quality_method: "A".into(),
713 reason_code: None,
714 reason_description: None,
715 update_datetime: NaiveDate::from_ymd(2004, 2, 2).and_hms(12, 0, 25),
716 msats_load_datetime: Some(NaiveDate::from_ymd(2004, 2, 2).and_hms(14, 25, 16)),
717 interval_events: None,
718 };
719
720 let raw = "300,20040201,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,1.111,A,,,20040202120025,20040202142516";
721
722 let res = record::IntervalData::parse(48, raw.into());
723
724 assert_eq!(res.map(|(i,v)|(i.into_fragment(),v)),Ok(("",interval_data)));
725 }
726
727 #[test]
728 fn interval_event_400() {
729 let interval_event = record::IntervalEvent {
730 start_interval: "1".into(),
731 end_interval: "20".into(),
732 quality_method: "F14".into(),
733 reason_code: Some("76".into()),
734 reason_description: None,
735 };
736
737 let raw = "400,1,20,F14,76,\n";
738 let res = record::IntervalEvent::parse(raw.into());
739 assert_eq!(res.map(|(i,v)|(i.into_fragment(),v)),Ok(("\n",interval_event)));
740
741 let interval_event = record::IntervalEvent {
742 start_interval: "25".into(),
743 end_interval: "48".into(),
744 quality_method: "S14".into(),
745 reason_code: Some("1".into()),
746 reason_description: None,
747 };
748
749 let raw = "400,25,48,S14,1,\n";
750 let res = record::IntervalEvent::parse(raw.into());
751 assert_eq!(res.map(|(i,v)|(i.into_fragment(),v)),Ok(("\n",interval_event)));
752
753 let interval_event = record::IntervalEvent {
754 start_interval: "21".into(),
755 end_interval: "24".into(),
756 quality_method: "A".into(),
757 reason_code: None,
758 reason_description: None,
759 };
760
761 let raw = "400,21,24,A,,\n";
762 let res = record::IntervalEvent::parse(raw.into());
763 assert_eq!(res.map(|(r,v)| (r.into_fragment(),v)),Ok(("\n",interval_event)));
764 }
765
766 #[test]
767 fn b2b_details_500() {
768 let interval_event = record::B2BDetails {
769 trans_code: "S".into(),
770 ret_service_order: "RETNSRVCEORD1".into(),
771 read_datetime: NaiveDate::from_ymd(2003,12,20).and_hms(15,45,0),
772 index_read: "001123.5".into(),
773 };
774
775 let raw = "500,S,RETNSRVCEORD1,20031220154500,001123.5\n";
776 let res = record::B2BDetails::parse(raw.into());
777 assert_eq!(res.map(|(r,v)| (r.into_fragment(),v)),Ok(("\n",interval_event)));
778 }
779
780 #[test]
781 fn end_of_data_900() {
782 let end_of_data = record::EndOfData {};
783
784 let raw = "900\n";
785 let res = record::EndOfData::parse(raw.into());
786 assert_eq!(res.map(|(r,v)| (r.into_fragment(),v)),Ok(("",end_of_data)));
787 }
788 }
789}