diff --git a/CHANGELOG.md b/CHANGELOG.md index e492ea1..4b199c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Fix WIN as default voice not working +### Changed +- **Breaking:** Carrier reports must now be prefixed with `CARRIER` instead of `ATIS`, eg.: `CARRIER Mother 250.000` + ## [1.0.0-alpha.10] - 2020-05-01 ### Added - Added default voice setting to DATIS settings diff --git a/README.md b/README.md index e471562..b743fd9 100644 --- a/README.md +++ b/README.md @@ -116,11 +116,10 @@ ATIS Kutaisi 251.000, TRAFFIC 252.000, VOICE WIN _I know, there is no such thing like a carrier ATIS in real life - though still a useful feature until we get the DCS carrier module._ -Setup like [Using Static Units](https://github.com/rkusa/DATIS#using-static-units), but instead of adding the `ATIS` name pattern to the name of a static unit, put it into the name of your carrier unt (not its group's name!) that should broadcast the report. +Setup like [Using Static Units](https://github.com/rkusa/DATIS#using-static-units), but instead of adding the `ATIS` name pattern to the name of a static unit, put it into the name of your carrier unit (not its group's name!) that should broadcast the report and prefix it with `CARRIER`. ![Example](./docs/carrier.jpg) -Known issue: It will broadcast on the unit's frequency and not on the frequency setup in the `ATIS` name pattern. ## Build diff --git a/crates/datis-core/src/lib.rs b/crates/datis-core/src/lib.rs index e432733..0ec61db 100644 --- a/crates/datis-core/src/lib.rs +++ b/crates/datis-core/src/lib.rs @@ -287,7 +287,7 @@ async fn audio_broadcast( Some(report) => report, None => { debug!( - "No report available right for station {}. Trying again in 30 seconds ...", + "No report available for station {}. Trying again in 30 seconds ...", station.name ); // postpone the next playback of the report by some seconds ... diff --git a/crates/datis-core/src/rpc.rs b/crates/datis-core/src/rpc.rs index c89bd13..db2a1c8 100644 --- a/crates/datis-core/src/rpc.rs +++ b/crates/datis-core/src/rpc.rs @@ -155,7 +155,7 @@ impl MissionRpc { match rx.await? { Response::Success(v) => Ok(Some(serde_json::from_value(v)?)), Response::Error(err) => { - error!("failed to get unit position: {}", err); + error!("failed to get position of unit {}: {}", name, err); Ok(None) } } @@ -172,7 +172,7 @@ impl MissionRpc { match rx.await? { Response::Success(v) => Ok(Some(serde_json::from_value(v)?)), Response::Error(err) => { - error!("failed to get unit heading: {}", err); + error!("failed to get heading of unit {}: {}", name, err); Ok(None) } } diff --git a/crates/datis-module/src/mission.rs b/crates/datis-module/src/mission.rs index adaaa6b..dad974a 100644 --- a/crates/datis-module/src/mission.rs +++ b/crates/datis-module/src/mission.rs @@ -89,7 +89,7 @@ pub fn extract(mut lua: Lua<'static>) -> Result { let mut get_mission_description: LuaFunction<_> = get!(dcs, "getMissionDescription")?; let mission_situation: String = get_mission_description.call()?; - extract_frequencies(&mission_situation) + extract_atis_station_frequencies(&mission_situation) }; // collect all airfields on the current loaded terrain @@ -304,7 +304,7 @@ pub fn extract(mut lua: Lua<'static>) -> Result { // check all units if they represent and ATIS station and if so, combine them with // their corresponding airfield stations.extend(mission_units.iter().filter_map(|mission_unit| { - extract_station_config(&mission_unit.name).and_then(|config| { + extract_atis_station_config(&mission_unit.name).and_then(|config| { airfields.remove(&config.name).map(|mut airfield| { airfield.traffic_freq = config.traffic; airfield.position.x = mission_unit.x; @@ -332,46 +332,79 @@ pub fn extract(mut lua: Lua<'static>) -> Result { } }; - stations.extend(mission_units.iter().filter_map(|mission_unit| { - extract_station_config(&mission_unit.name).map(|config| Station { - name: config.name.clone(), - freq: config.atis, - tts: config.tts.unwrap_or_else(|| default_voice.clone()), - transmitter: Transmitter::Carrier(Carrier { - name: config.name, - unit_id: mission_unit.id, - unit_name: mission_unit.name.clone(), - }), - rpc: Some(rpc.clone()), - }) - })); + if stations.is_empty() { + info!("No ATIS stations found ..."); + } else { + info!("ATIS Stations:"); + for station in &stations { + info!( + " - {} (Freq: {}, Voice: {:?})", + station.name, station.freq, station.tts + ); + } + } - stations.extend(mission_units.iter().filter_map(|mission_unit| { - extract_custom_broadcast_config(&mission_unit.name).map(|config| Station { - name: mission_unit.name.clone(), - freq: config.freq, - tts: config.tts.unwrap_or_else(|| default_voice.clone()), - transmitter: Transmitter::Custom(Custom { - unit_id: mission_unit.id, - unit_name: mission_unit.name.clone(), - message: config.message, - }), - rpc: Some(rpc.clone()), + let carriers = mission_units + .iter() + .filter_map(|mission_unit| { + extract_carrier_station_config(&mission_unit.name).map(|config| Station { + name: config.name.clone(), + freq: config.atis, + tts: config.tts.unwrap_or_else(|| default_voice.clone()), + transmitter: Transmitter::Carrier(Carrier { + name: config.name, + unit_id: mission_unit.id, + unit_name: mission_unit.name.clone(), + }), + rpc: Some(rpc.clone()), + }) }) - })); - - debug!("Valid ATIS Stations:"); - for station in &stations { - debug!( - " - {} (Freq: {}, Voice: {:?})", - station.name, station.freq, station.tts - ); + .collect::>(); + + if carriers.is_empty() { + info!("No Carrier stations found ..."); + } else { + info!("Carrier Stations:"); + for station in &carriers { + info!( + " - {} (Freq: {}, Voice: {:?})", + station.name, station.freq, station.tts + ); + } } - if stations.is_empty() { - warn!("No ATIS stations found ..."); + let broadcasts = mission_units + .iter() + .filter_map(|mission_unit| { + extract_custom_broadcast_config(&mission_unit.name).map(|config| Station { + name: mission_unit.name.clone(), + freq: config.freq, + tts: config.tts.unwrap_or_else(|| default_voice.clone()), + transmitter: Transmitter::Custom(Custom { + unit_id: mission_unit.id, + unit_name: mission_unit.name.clone(), + message: config.message, + }), + rpc: Some(rpc.clone()), + }) + }) + .collect::>(); + + if broadcasts.is_empty() { + info!("No custom Broadcast stations found ..."); + } else { + info!("Broadcast Stations:"); + for station in &broadcasts { + info!( + " - {} (Freq: {}, Voice: {:?})", + station.name, station.freq, station.tts + ); + } } + stations.extend(carriers); + stations.extend(broadcasts); + Ok(Info { stations, gcloud_key, @@ -405,7 +438,7 @@ struct StationConfig { tts: Option, } -fn extract_frequencies(situation: &str) -> HashMap { +fn extract_atis_station_frequencies(situation: &str) -> HashMap { // extract ATIS stations and frequencies let re = Regex::new(r"ATIS ([a-zA-Z- ]+) ([1-3]\d{2}(\.\d{1,3})?)").unwrap(); let mut stations: HashMap = re @@ -441,7 +474,7 @@ fn extract_frequencies(situation: &str) -> HashMap { stations } -fn extract_station_config(config: &str) -> Option { +fn extract_atis_station_config(config: &str) -> Option { let re = RegexBuilder::new( r"^ATIS ([a-zA-Z- ]+) ([1-3]\d{2}(\.\d{1,3})?)(,[ ]?TRAFFIC ([1-3]\d{2}(\.\d{1,3})?))?(,[ ]?VOICE ([a-zA-Z-:]+))?$", ) @@ -467,6 +500,30 @@ fn extract_station_config(config: &str) -> Option { }) } +fn extract_carrier_station_config(config: &str) -> Option { + let re = RegexBuilder::new( + r"^CARRIER ([a-zA-Z- ]+) ([1-3]\d{2}(\.\d{1,3})?)(,[ ]?VOICE ([a-zA-Z-:]+))?$", + ) + .case_insensitive(true) + .build() + .unwrap(); + re.captures(config).map(|caps| { + dbg!(&caps); + let name = caps.get(1).unwrap().as_str(); + let atis_freq = caps.get(2).unwrap().as_str(); + let atis_freq = (f64::from_str(atis_freq).unwrap() * 1_000_000.0) as u64; + let tts = caps + .get(5) + .and_then(|s| TextToSpeechProvider::from_str(s.as_str()).ok()); + StationConfig { + name: name.to_string(), + atis: atis_freq, + traffic: None, + tts, + } + }) +} + #[derive(Debug, PartialEq)] struct BroadcastConfig { freq: u64, @@ -482,7 +539,6 @@ fn extract_custom_broadcast_config(config: &str) -> Option { .build() .unwrap(); re.captures(config).map(|caps| { - dbg!(&caps); let freq = caps.get(1).unwrap().as_str(); let freq = (f32::from_str(freq).unwrap() * 1_000_000.0) as u64; let tts = caps @@ -500,14 +556,15 @@ fn extract_custom_broadcast_config(config: &str) -> Option { #[cfg(test)] mod test { use super::{ - extract_custom_broadcast_config, extract_frequencies, extract_station_config, - BroadcastConfig, StationConfig, + extract_atis_station_config, extract_atis_station_frequencies, + extract_carrier_station_config, extract_custom_broadcast_config, BroadcastConfig, + StationConfig, }; use datis_core::tts::{aws, gcloud, TextToSpeechProvider}; #[test] fn test_mission_situation_extraction() { - let freqs = extract_frequencies( + let freqs = extract_atis_station_frequencies( r#" ATIS Mineralnye Vody 251.000 ATIS Batumi 131.5 @@ -554,9 +611,9 @@ mod test { } #[test] - fn test_config_extraction() { + fn test_atis_config_extraction() { assert_eq!( - extract_station_config("ATIS Kutaisi 251"), + extract_atis_station_config("ATIS Kutaisi 251"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 251_000_000, @@ -566,7 +623,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Mineralnye Vody 251"), + extract_atis_station_config("ATIS Mineralnye Vody 251"), Some(StationConfig { name: "Mineralnye Vody".to_string(), atis: 251_000_000, @@ -576,7 +633,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Senaki-Kolkhi 251"), + extract_atis_station_config("ATIS Senaki-Kolkhi 251"), Some(StationConfig { name: "Senaki-Kolkhi".to_string(), atis: 251_000_000, @@ -586,7 +643,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Kutaisi 251.000, TRAFFIC 123.45"), + extract_atis_station_config("ATIS Kutaisi 251.000, TRAFFIC 123.45"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 251_000_000, @@ -596,7 +653,9 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Kutaisi 251.000, TRAFFIC 123.45, VOICE en-US-Standard-E"), + extract_atis_station_config( + "ATIS Kutaisi 251.000, TRAFFIC 123.45, VOICE en-US-Standard-E" + ), Some(StationConfig { name: "Kutaisi".to_string(), atis: 251_000_000, @@ -608,7 +667,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Kutaisi 251.000, VOICE en-US-Standard-E"), + extract_atis_station_config("ATIS Kutaisi 251.000, VOICE en-US-Standard-E"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 251_000_000, @@ -620,7 +679,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Kutaisi 131.400"), + extract_atis_station_config("ATIS Kutaisi 131.400"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 131_400_000, @@ -630,10 +689,45 @@ mod test { ); } + #[test] + fn test_carrier_config_extraction() { + assert_eq!( + extract_carrier_station_config("CARRIER Mother 251"), + Some(StationConfig { + name: "Mother".to_string(), + atis: 251_000_000, + traffic: None, + tts: None, + }) + ); + + assert_eq!( + extract_carrier_station_config("CARRIER Mother 131.400"), + Some(StationConfig { + name: "Mother".to_string(), + atis: 131_400_000, + traffic: None, + tts: None, + }) + ); + + assert_eq!( + extract_carrier_station_config("CARRIER Mother 251.000, VOICE en-US-Standard-E"), + Some(StationConfig { + name: "Mother".to_string(), + atis: 251_000_000, + traffic: None, + tts: Some(TextToSpeechProvider::GoogleCloud { + voice: gcloud::VoiceKind::StandardE + }), + }) + ); + } + #[test] fn test_cloud_provider_prefix_extraction() { assert_eq!( - extract_station_config("ATIS Kutaisi 131.400, VOICE GC:en-US-Standard-D"), + extract_atis_station_config("ATIS Kutaisi 131.400, VOICE GC:en-US-Standard-D"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 131_400_000, @@ -645,7 +739,7 @@ mod test { ); assert_eq!( - extract_station_config("ATIS Kutaisi 131.400, VOICE AWS:Brian"), + extract_atis_station_config("ATIS Kutaisi 131.400, VOICE AWS:Brian"), Some(StationConfig { name: "Kutaisi".to_string(), atis: 131_400_000, diff --git a/docs/carrier.jpg b/docs/carrier.jpg index e222877..d9b7f21 100644 Binary files a/docs/carrier.jpg and b/docs/carrier.jpg differ