1use std::fmt;
2use std::num::{NonZeroU16, NonZeroU32};
3use std::str::FromStr;
4
5use anyhow::{Result, anyhow};
6use serde::{Deserialize, Deserializer};
7
8use crate::consts::NOMINAL_FREQUENCY_HZ;
9
10#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
12pub enum SysStream {
13 Time,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct SvStreamId {
19 pub appid: u16,
20 pub svid: String,
21 pub simulated: bool,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub enum StreamId {
27 Sv {
28 id: SvStreamId,
29 },
30 App {
31 app_name: String,
32 stream_name: String,
33 },
34 Sys {
35 stream: SysStream,
36 },
37 Goose {
38 go_id: String,
39 },
40}
41
42impl StreamId {
43 pub const SYS_TIME: Self = Self::Sys {
44 stream: SysStream::Time,
45 };
46}
47
48impl FromStr for StreamId {
49 type Err = String;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 let parts: Vec<&str> = s.split(':').collect();
53 match parts.as_slice() {
54 ["App", app_name, stream_name] => {
55 let app_name = app_name.to_string();
56 let stream_name = stream_name.to_string();
57 Ok(StreamId::App {
58 app_name,
59 stream_name,
60 })
61 }
62 ["SV", appid, svid] | ["SV", appid, svid, "test"] => {
63 let appid = u16::from_str_radix(appid, 16)
64 .map_err(|_| "Invalid hex integer for appid".to_string())?;
65 let simulated = parts.len() > 3;
66 Ok(StreamId::Sv {
67 id: SvStreamId {
68 appid,
69 svid: svid.to_string(),
70 simulated,
71 },
72 })
73 }
74 ["Sys", "Time"] => Ok(StreamId::Sys {
75 stream: SysStream::Time,
76 }),
77 ["GOOSE", go_id @ ..] => Ok(StreamId::Goose {
78 go_id: go_id.join(":"),
79 }),
80 _ => Err(format!("Invalid format StreamId: {s}").to_string()),
81 }
82 }
83}
84
85impl fmt::Display for SysStream {
86 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 match self {
88 SysStream::Time => write!(f, "Time"),
89 }
90 }
91}
92
93impl fmt::Display for SvStreamId {
94 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
95 write!(
96 f,
97 "SV:{:x}:{}{}",
98 self.appid,
99 self.svid,
100 if self.simulated { ":test" } else { "" }
101 )
102 }
103}
104
105impl fmt::Display for StreamId {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 match self {
108 StreamId::App {
109 app_name,
110 stream_name,
111 } => {
112 write!(f, "App:{app_name}:{stream_name}")
113 }
114 StreamId::Sv { id } => fmt::Display::fmt(id, f),
115 StreamId::Sys { stream } => {
116 write!(f, "Sys:{stream}")
117 }
118 StreamId::Goose { go_id } => {
119 write!(f, "GOOSE:{go_id}")
120 }
121 }
122 }
123}
124
125impl<'de> Deserialize<'de> for StreamId {
126 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127 where
128 D: Deserializer<'de>,
129 {
130 let s: String = Deserialize::deserialize(deserializer)?;
131 StreamId::from_str(&s).map_err(serde::de::Error::custom)
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
137#[serde(rename_all = "snake_case")]
138pub enum SampleRate {
139 SamplesPerSecond(NonZeroU32),
140 SecondsPerSample(NonZeroU32),
141 SamplesPerPeriod(NonZeroU32),
142
143 SamplesPerCycle(NonZeroU32),
145 Variable(String),
146}
147
148impl SampleRate {
149 pub fn to_samples_per_second(&self) -> Result<NonZeroU16> {
154 fn to_nonzero_u16(val: u32, field: &'static str) -> Result<NonZeroU16> {
155 let rate = u16::try_from(val).map_err(|_| anyhow!("{field} must fit in non-zero u16"))?;
156 Ok(NonZeroU16::new(rate).expect("NonZeroU32 converted to u16 cannot become zero"))
157 }
158
159 match self {
160 SampleRate::SamplesPerSecond(val) => {
161 to_nonzero_u16(val.get(), "sample_rate.samples_per_second")
162 }
163 SampleRate::SamplesPerPeriod(val) | SampleRate::SamplesPerCycle(val) => {
166 let rate = val
167 .get()
168 .checked_mul(u32::from(NOMINAL_FREQUENCY_HZ.get()))
169 .ok_or_else(|| {
170 anyhow!("overflow in sample_rate.samples_per_period * nominal_frequency")
171 })?;
172 to_nonzero_u16(rate, "sample_rate.samples_per_period * nominal_frequency")
173 }
174 SampleRate::SecondsPerSample(_) => Err(anyhow!(
175 "sample_rate.seconds_per_sample cannot be converted to samples per second"
176 )),
177 SampleRate::Variable(_) => Err(anyhow!("sample_rate.variable has no fixed rate")),
178 }
179 }
180}
181
182#[derive(Debug, Clone, Deserialize)]
184pub enum StreamValueType {
185 Voltage,
186 Current,
187 Energy,
188 Power,
189 Frequency,
190 Other,
191}
192
193#[derive(Debug, Clone, Deserialize)]
195pub enum SIUnit {
196 Ampere,
197 Volt,
198 Radian,
199 Watt,
200 Joule,
201 Hz,
202 VArs,
203 SIOther,
204}
205
206#[derive(Debug, Clone, Deserialize)]
208pub struct StreamLayoutValue {
209 pub name: String,
210 pub r#type: StreamValueType,
211 pub unit: SIUnit,
212 pub mag: i64,
213}
214
215#[derive(Debug, Clone, Deserialize)]
217pub struct DataStreamInfo {
218 pub name: String,
219 pub sample_rate: SampleRate,
220 pub fields: Vec<StreamLayoutValue>,
221}
222
223impl DataStreamInfo {
224 pub fn find_field(&self, name: &str) -> Result<(usize, &StreamLayoutValue), std::io::Error> {
226 self
227 .fields
228 .iter()
229 .enumerate()
230 .find(|(_, f)| f.name == name)
231 .ok_or_else(|| {
232 std::io::Error::other(format!(
233 "Field '{}' not found in stream '{}'",
234 name, self.name
235 ))
236 })
237 }
238
239 pub fn find_field_idx(&self, name: &str) -> Result<usize, std::io::Error> {
241 self.find_field(name).map(|(idx, _)| idx)
242 }
243}
244
245#[derive(Debug, Clone, Deserialize)]
247pub struct GooseStreamInfo {
248 pub name: String,
249}
250
251#[derive(Debug, Clone, Deserialize)]
253pub struct SysStreamInfo {
254 pub name: String,
255}
256
257#[derive(Debug, Clone, Deserialize)]
260#[serde(rename_all = "snake_case")]
261pub enum StreamInfo {
262 DataStreamInfo(DataStreamInfo),
263 GooseStreamInfo(GooseStreamInfo),
264 SysStreamInfo(SysStreamInfo),
265}
266
267impl StreamInfo {
268 pub fn name(&self) -> String {
270 match self {
271 Self::DataStreamInfo(info) => info.name.clone(),
272 Self::GooseStreamInfo(info) => info.name.clone(),
273 Self::SysStreamInfo(info) => info.name.clone(),
274 }
275 }
276}
277
278#[derive(Debug, Clone, Deserialize)]
281pub struct InputStream {
282 #[serde(rename = "$input_stream")]
283 pub name: StreamId,
284}
285
286#[derive(Debug, Clone, Deserialize)]
288pub struct OutputStream {
289 #[serde(rename = "$output_stream")]
290 pub name: String,
291 pub sample_rate: SampleRate,
292 pub fields: Vec<StreamLayoutValue>,
293}
294
295#[derive(Debug, Clone, Deserialize)]
296pub struct OwnedTrigger {
297 #[serde(rename = "$trigger")]
298 pub id: String,
299}
300
301#[derive(Debug, Clone, Deserialize)]
302pub struct SendTrigger {
303 #[serde(rename = "$send_trigger")]
304 pub id: String,
305}