Skip to content

Commit c5292b4

Browse files
committed
Merge branch 'feature/new_events' into develop
sameold,samedec: New EventCode API Previous versions of the `EventCode` API had two problems: 1. The conversion from `MessageHeader` → `EventCode` was fallible, even though messages with unrecognized event codes should still be played. 2. Since each SAME event code was its own `EventCode`, it was not possible to separate the phenomenon from the severity. This PR is a major overhaul of all enums used to decode SAME event codes. Originator: * Improved detection of National Weather Service vs Environment Canada. The `WeatherService` enum variant is dropped. * Rename `Originator::as_str()` to `Originator::as_code_str()` for clarity * Support the "alternate" format code. * Originator is no longer `From<&str>`. Instead, it should be constructed via `Originator::from_org_and_call()`. SignificanceLevel: * `SignificanceLevel::from()` constructs directly from significance code str. The conversion is now infallible. * A new `SignificanceLevel::Unknown` variant is returned when a significance level cannot be determined. It sorts higher than `Warning`. * The UnknownSignificanceLevel is no longer required and is removed. * Renamed `SignificanceLevel::as_str()` → `SignificanceLevel::as_code_str()`. This method returns a one-character significance code. * The following messages are upgraded to `Statement`: * ADR Administrative Message * NMN Network Message Notification Phenomenon: * The enum of all the SAME events is renamed to `Phenomenon`. * Phenomenon previously de-normalized into significance levels, like `TornadoWatch` and `TornadoWarning`, are represented as a single enum entry like `Tornado`. * Add Phenomenon from the 2022 revision of NWSI 10-1712 and more Canadian codes: * EAN: Renamed to National Emergency Message * NAT: National Audible Test * NST: National Silent Test * FSW: Flash Freeze Warning * FZW: Freeze Warning * HLS: Hurricane Local Statement * SQW: Snow Squall Warning * Add basic categories of Phenomenon * Fix bad code entry for ADR (Administrative Message) EventCode: * The `EventCode` is now a struct which represents the combination of a `Phenomenon` and a `SignificanceLevel`. * `EventCode` no longer convert directly to static str. Formatting requires either `Display` or `to_string()`. The human-readable string output for each SAME event code is largely unchanged. Remove `EventCode::as_display_str()` and replace with `to_display_string()`. * The alternate formatter for `EventCode` now displays only the phenomenon, like `Tornado`, without the significance level * `EventCode` are now Ord by their significance levels * `EventCode` no longer convert back to their original SAME event code string. Use `MessageHeader::event_str()` instead. This is an API-BREAKING change for sameold but preserves the samedec CLI.
2 parents 98b4491 + ec9f691 commit c5292b4

File tree

15 files changed

+1536
-832
lines changed

15 files changed

+1536
-832
lines changed

Cargo.lock

Lines changed: 64 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/samedec/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,15 @@ The child process receives the following additional environment variables:
273273
274274
* `SAMEDEC_EVT`: the three-character SAME event code, like "`RWT`"
275275
276-
* `SAMEDEC_EVENT`: human-readable event name: "`Required Weekly Test`." If the
277-
event code is not known, and it its significance level is also unknown, then
278-
this string will be "`Unrecognized`."
276+
* `SAMEDEC_EVENT`: human-readable event description, including its significance
277+
level: "`Required Weekly Test`." If the event code is not known, and it its
278+
significance level is also unknown, then this string will be
279+
"`Unrecognized Warning`."
279280
280281
* `SAMEDEC_SIGNIFICANCE`: one-character significance level. This variable will
281282
be empty if the significance level could not be determined (i.e., because
282283
the event code is unknown).
284+
283285
* `T`: Test
284286
* `M`: Message
285287
* `S`: Statement

crates/samedec/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ mod tests {
260260
use super::*;
261261

262262
use chrono::{Duration, TimeZone, Utc};
263-
use sameold::EventCode;
263+
use sameold::Phenomenon;
264264

265265
#[test]
266266
fn test_make_demo_message() {
@@ -270,7 +270,7 @@ mod tests {
270270
Message::StartOfMessage(hdr) => hdr,
271271
_ => unreachable!(),
272272
};
273-
assert_eq!(msg.event().unwrap(), EventCode::PracticeDemoWarning);
273+
assert_eq!(msg.event().phenomenon(), Phenomenon::PracticeDemoWarning);
274274
assert_eq!(msg.issue_datetime(&tm).unwrap(), tm);
275275
assert_eq!(msg.valid_duration(), Duration::minutes(15));
276276
}

crates/samedec/src/spawner.rs

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::io;
77
use std::process::{Child, Command, Stdio};
88

99
use chrono::{DateTime, Utc};
10-
use sameold::{MessageHeader, UnrecognizedEventCode};
10+
use sameold::MessageHeader;
1111

1212
/// Spawn a child process to handle the given message
1313
///
@@ -54,8 +54,11 @@ where
5454
header.originator().as_display_str(),
5555
)
5656
.env(childenv::SAMEDEC_EVT, header.event_str())
57-
.env(childenv::SAMEDEC_EVENT, msg_to_event(&header))
58-
.env(childenv::SAMEDEC_SIGNIFICANCE, msg_to_significance(header))
57+
.env(childenv::SAMEDEC_EVENT, header.event().to_string())
58+
.env(
59+
childenv::SAMEDEC_SIGNIFICANCE,
60+
header.event().significance().as_code_str(),
61+
)
5962
.env(childenv::SAMEDEC_LOCATIONS, locations.join(" "))
6063
.env(childenv::SAMEDEC_ISSUETIME, issue_ts)
6164
.env(childenv::SAMEDEC_PURGETIME, purge_ts)
@@ -143,23 +146,6 @@ mod childenv {
143146
pub const SAMEDEC_PURGETIME: &str = "SAMEDEC_PURGETIME";
144147
}
145148

146-
// convert message event code to string
147-
fn msg_to_event(msg: &MessageHeader) -> String {
148-
match msg.event() {
149-
Ok(evt) => evt.as_display_str().to_owned(),
150-
Err(err) => format!("{}", err),
151-
}
152-
}
153-
154-
// convert message event code to significance
155-
fn msg_to_significance(msg: &MessageHeader) -> &'static str {
156-
match msg.event() {
157-
Ok(evt) => evt.to_significance_level().as_str(),
158-
Err(UnrecognizedEventCode::WithSignificance(sl)) => sl.as_str(),
159-
Err(UnrecognizedEventCode::Unrecognized) => "",
160-
}
161-
}
162-
163149
// convert DateTime to UTC unix timestamp in seconds, as string
164150
fn time_to_unix_str(tm: DateTime<Utc>) -> String {
165151
format!("{}", tm.format("%s"))
@@ -169,15 +155,6 @@ fn time_to_unix_str(tm: DateTime<Utc>) -> String {
169155
mod tests {
170156
use super::*;
171157

172-
use sameold::MessageHeader;
173-
174-
#[test]
175-
fn test_msg_to_significance() {
176-
const MSG: &str = "ZCZC-WXR-RWT-012345-567890-888990+0351-3662322-NOCALL-";
177-
let msg = MessageHeader::new(MSG).unwrap();
178-
assert_eq!("T", msg_to_significance(&msg));
179-
}
180-
181158
#[test]
182159
fn test_time_to_unix_str() {
183160
let dt: DateTime<Utc> = DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT")

crates/sameold/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ log = "0.4"
1919
nalgebra = "^0.32.2"
2020
num-complex = "^0.3.1"
2121
num-traits = "^0.2"
22+
phf = {version = "^0.11", features = ["macros"]}
2223
regex = "^1.5.5"
2324
slice-ring-buffer = "^0.3"
2425
strum = "^0.21"

crates/sameold/README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ carrier signals before and during message decoding.
129129

130130
### Interpreting Messages
131131

132-
The [`Message`] type marks the start or end of a SAME message. The
133-
actual "message" part of a SAME message is the audio itself, which
134-
should contain a voice message that
132+
The [`Message`](https://docs.rs/sameold/latest/sameold/enum.Message.html) type
133+
marks the start or end of a SAME message. The actual "message" part of a SAME
134+
message is the audio itself, which should contain a voice message that
135135

136136
* describes the event; and
137137
* provides instructions to the listener.
@@ -148,21 +148,25 @@ If this was the header string received, then you could decode
148148
`hdr` from the previous example as follows:
149149

150150
```rust
151-
use sameold::{EventCode, Originator, SignificanceLevel};
151+
use sameold::{Phenomenon, Originator, SignificanceLevel};
152152

153153
// what organization originated the message?
154154
assert_eq!(Originator::NationalWeatherService, hdr.originator());
155155

156-
// event code
157-
// in actual implementations, handle this error gracefully!
158-
let evt = hdr.event().expect("unknown event code");
159-
assert_eq!(EventCode::RequiredWeeklyTest, evt);
156+
// parse SAME event code `RWT`
157+
let evt = hdr.event();
160158

161-
// events have a "significance level" which describes how
162-
// urgent or actual they are
163-
assert_eq!(SignificanceLevel::Test, evt.to_significance_level());
159+
// the Phenomenon describes what is occurring
160+
assert_eq!(Phenomenon::RequiredWeeklyTest, evt.phenomenon());
161+
162+
// the SignificanceLevel indicates the overall severity and/or
163+
// how intrusive or noisy the alert should be
164+
assert_eq!(SignificanceLevel::Test, evt.significance());
164165
assert!(SignificanceLevel::Test < SignificanceLevel::Warning);
165166

167+
// Display to the user
168+
assert_eq!("Required Weekly Test", &format!("{}", evt));
169+
166170
// location codes are accessed by iterator
167171
let first_location = hdr.location_str_iter().next();
168172
assert_eq!(Some("012345"), first_location);
@@ -171,10 +175,12 @@ assert_eq!(Some("012345"), first_location);
171175
SAME messages are always transmitted three times for redundancy.
172176
When decoding the message header, `sameold` will use all three
173177
transmissions together to improve decoding. Only one
174-
[`Message::StartOfMessage`] is output for all three header transmissions.
178+
[`Message::StartOfMessage`](https://docs.rs/sameold/latest/sameold/enum.Message.html#variant.StartOfMessage)
179+
is output for all three header transmissions.
175180
The trailers which denote the end of the message are **not** subject to
176-
this error-correction process. One [`Message::EndOfMessage`] is
177-
output for every trailer received. There may be up to three
181+
this error-correction process. One
182+
[`Message::EndOfMessage`](https://docs.rs/sameold/latest/sameold/enum.Message.html#variant.EndOfMessage)
183+
is output for every trailer received. There may be up to three
178184
`EndOfMessage` output for every complete SAME message.
179185

180186
## Background

0 commit comments

Comments
 (0)