Skip to main content

spvirit_server/
apply.rs

1//! Functions that apply decoded PUT values to Normative Type payloads.
2
3use spvirit_codec::spvd_decode::DecodedValue;
4use spvirit_types::*;
5
6use crate::convert::*;
7
8/// Apply a scalar value update from a decoded PUT body to an `NtScalar`.
9pub fn apply_value_update(nt: &mut NtScalar, val: &DecodedValue, compute_alarms: bool) -> bool {
10    if let DecodedValue::Structure(fields) = val {
11        if let Some((_, inner)) = fields.iter().find(|(name, _)| name == "value") {
12            return apply_value_update(nt, inner, compute_alarms);
13        }
14    }
15    match &mut nt.value {
16        ScalarValue::Bool(current) => {
17            if let Some(v) = decoded_to_bool(val) {
18                *current = v;
19                if compute_alarms {
20                    nt.update_alarm_from_value();
21                }
22                return true;
23            }
24        }
25        ScalarValue::I32(current) => {
26            if let Some(v) = decoded_to_i32(val) {
27                *current = v;
28                if compute_alarms {
29                    nt.update_alarm_from_value();
30                }
31                return true;
32            }
33        }
34        ScalarValue::F64(current) => {
35            if let Some(v) = decoded_to_f64(val) {
36                *current = v;
37                if compute_alarms {
38                    nt.update_alarm_from_value();
39                }
40                return true;
41            }
42        }
43        ScalarValue::Str(current) => {
44            if let Some(v) = decoded_to_string(val) {
45                *current = v;
46                if compute_alarms {
47                    nt.update_alarm_from_value();
48                }
49                return true;
50            }
51        }
52        _ => {
53            if let Some(v) = decoded_to_f64(val) {
54                match &mut nt.value {
55                    ScalarValue::I8(c) => { *c = v as i8; }
56                    ScalarValue::I16(c) => { *c = v as i16; }
57                    ScalarValue::I64(c) => { *c = v as i64; }
58                    ScalarValue::U8(c) => { *c = v as u8; }
59                    ScalarValue::U16(c) => { *c = v as u16; }
60                    ScalarValue::U32(c) => { *c = v as u32; }
61                    ScalarValue::U64(c) => { *c = v as u64; }
62                    ScalarValue::F32(c) => { *c = v as f32; }
63                    _ => return false,
64                }
65                if compute_alarms {
66                    nt.update_alarm_from_value();
67                }
68                return true;
69            }
70        }
71    }
72    false
73}
74
75/// Apply an alarm structure update to an `NtScalar`.
76pub fn apply_alarm_update(nt: &mut NtScalar, val: &DecodedValue) -> bool {
77    let DecodedValue::Structure(fields) = val else {
78        return false;
79    };
80    let mut changed = false;
81    for (name, v) in fields {
82        match name.as_str() {
83            "severity" => {
84                if let Some(i) = decoded_to_i32(v) {
85                    nt.alarm_severity = i;
86                    changed = true;
87                }
88            }
89            "status" => {
90                if let Some(i) = decoded_to_i32(v) {
91                    nt.alarm_status = i;
92                    changed = true;
93                }
94            }
95            "message" => {
96                if let Some(s) = decoded_to_string(v) {
97                    nt.alarm_message = s;
98                    changed = true;
99                }
100            }
101            _ => {}
102        }
103    }
104    changed
105}
106
107/// Apply a display structure update to an `NtScalar`.
108pub fn apply_display_update(nt: &mut NtScalar, val: &DecodedValue) -> bool {
109    let DecodedValue::Structure(fields) = val else {
110        return false;
111    };
112    let mut changed = false;
113    for (name, v) in fields {
114        match name.as_str() {
115            "low" | "limitLow" => {
116                if let Some(f) = decoded_to_f64(v) {
117                    nt.display_low = f;
118                    changed = true;
119                }
120            }
121            "high" | "limitHigh" => {
122                if let Some(f) = decoded_to_f64(v) {
123                    nt.display_high = f;
124                    changed = true;
125                }
126            }
127            "description" => {
128                if let Some(s) = decoded_to_string(v) {
129                    nt.display_description = s;
130                    changed = true;
131                }
132            }
133            "units" => {
134                if let Some(s) = decoded_to_string(v) {
135                    nt.units = s;
136                    changed = true;
137                }
138            }
139            "precision" => {
140                if let Some(i) = decoded_to_i32(v) {
141                    nt.display_precision = i;
142                    changed = true;
143                }
144            }
145            "form" => {
146                if let DecodedValue::Structure(form_fields) = v {
147                    let mut updated = false;
148                    for (fname, fval) in form_fields {
149                        match fname.as_str() {
150                            "index" => {
151                                if let Some(i) = decoded_to_i32(fval) {
152                                    nt.display_form_index = i;
153                                    updated = true;
154                                }
155                            }
156                            "choices" => {
157                                if let DecodedValue::Array(items) = fval {
158                                    let mut choices = Vec::new();
159                                    for item in items {
160                                        if let DecodedValue::String(s) = item {
161                                            choices.push(s.clone());
162                                        }
163                                    }
164                                    if !choices.is_empty() {
165                                        nt.display_form_choices = choices;
166                                        updated = true;
167                                    }
168                                }
169                            }
170                            _ => {}
171                        }
172                    }
173                    if updated {
174                        changed = true;
175                    }
176                }
177            }
178            _ => {}
179        }
180    }
181    changed
182}
183
184/// Apply a control structure update to an `NtScalar`.
185pub fn apply_control_update(nt: &mut NtScalar, val: &DecodedValue) -> bool {
186    let DecodedValue::Structure(fields) = val else {
187        return false;
188    };
189    let mut changed = false;
190    for (name, v) in fields {
191        match name.as_str() {
192            "low" | "limitLow" => {
193                if let Some(f) = decoded_to_f64(v) {
194                    nt.control_low = f;
195                    changed = true;
196                }
197            }
198            "high" | "limitHigh" => {
199                if let Some(f) = decoded_to_f64(v) {
200                    nt.control_high = f;
201                    changed = true;
202                }
203            }
204            "minStep" => {
205                if let Some(f) = decoded_to_f64(v) {
206                    nt.control_min_step = f;
207                    changed = true;
208                }
209            }
210            _ => {}
211        }
212    }
213    changed
214}
215
216/// Apply a scalar-array PUT update to an `NtScalarArray`.
217pub fn apply_scalar_array_put(
218    nt: &mut NtScalarArray,
219    nord: &mut usize,
220    value: &DecodedValue,
221) -> bool {
222    let field_value = match value {
223        DecodedValue::Structure(fields) => fields
224            .iter()
225            .find(|(name, _)| name == "value")
226            .map(|(_, v)| v)
227            .unwrap_or(value),
228        _ => value,
229    };
230    if let Some(next) = decoded_to_scalar_array(field_value, &nt.value) {
231        let changed = nt.value != next;
232        if changed {
233            *nord = next.len();
234            nt.value = next;
235        }
236        return changed;
237    }
238    false
239}
240
241/// Apply a table PUT update to an `NtTable`.
242pub fn apply_table_put(nt: &mut NtTable, value: &DecodedValue) -> bool {
243    let DecodedValue::Structure(fields) = value else {
244        return false;
245    };
246    let mut changed = false;
247    for (name, field_value) in fields {
248        match name.as_str() {
249            "labels" => {
250                if let DecodedValue::Array(items) = field_value {
251                    let labels: Vec<String> = items.iter().filter_map(decoded_to_string).collect();
252                    if !labels.is_empty() && nt.labels != labels {
253                        nt.labels = labels;
254                        changed = true;
255                    }
256                }
257            }
258            "value" => {
259                if let DecodedValue::Structure(cols) = field_value {
260                    for (col_name, col_value) in cols {
261                        if let Some(col) = nt.columns.iter_mut().find(|c| c.name == *col_name) {
262                            if let Some(next) = decoded_to_scalar_array(col_value, &col.values) {
263                                if col.values != next {
264                                    col.values = next;
265                                    changed = true;
266                                }
267                            }
268                        }
269                    }
270                }
271            }
272            "descriptor" => {
273                if let Some(s) = decoded_to_string(field_value) {
274                    let next = if s.is_empty() { None } else { Some(s) };
275                    if nt.descriptor != next {
276                        nt.descriptor = next;
277                        changed = true;
278                    }
279                }
280            }
281            "alarm" => {
282                if let Some(alarm) = decode_nt_alarm(field_value) {
283                    if nt.alarm.as_ref() != Some(&alarm) {
284                        nt.alarm = Some(alarm);
285                        changed = true;
286                    }
287                }
288            }
289            "timeStamp" => {
290                if let Some(ts) = decode_nt_timestamp(field_value) {
291                    if nt.time_stamp.as_ref() != Some(&ts) {
292                        nt.time_stamp = Some(ts);
293                        changed = true;
294                    }
295                }
296            }
297            _ => {}
298        }
299    }
300    changed
301}
302
303/// Apply an NdArray PUT update to an `NtNdArray`.
304pub fn apply_ndarray_put(nt: &mut NtNdArray, value: &DecodedValue) -> bool {
305    let DecodedValue::Structure(fields) = value else {
306        return false;
307    };
308    let mut changed = false;
309    for (name, field_value) in fields {
310        match name.as_str() {
311            "value" => {
312                if let Some(next) = decoded_to_scalar_array(field_value, &nt.value) {
313                    if nt.value != next {
314                        nt.value = next;
315                        changed = true;
316                    }
317                }
318            }
319            "compressedSize" => {
320                if let Some(v) = decoded_to_i64(field_value) {
321                    if nt.compressed_size != v {
322                        nt.compressed_size = v;
323                        changed = true;
324                    }
325                }
326            }
327            "uncompressedSize" => {
328                if let Some(v) = decoded_to_i64(field_value) {
329                    if nt.uncompressed_size != v {
330                        nt.uncompressed_size = v;
331                        changed = true;
332                    }
333                }
334            }
335            "uniqueId" => {
336                if let Some(v) = decoded_to_i32(field_value) {
337                    if nt.unique_id != v {
338                        nt.unique_id = v;
339                        changed = true;
340                    }
341                }
342            }
343            "codec" => {
344                if let DecodedValue::Structure(codec_fields) = field_value {
345                    for (cname, cval) in codec_fields {
346                        if cname == "name" {
347                            if let Some(s) = decoded_to_string(cval) {
348                                if nt.codec.name != s {
349                                    nt.codec.name = s;
350                                    changed = true;
351                                }
352                            }
353                        }
354                    }
355                }
356            }
357            "dimension" => {
358                if let DecodedValue::Array(items) = field_value {
359                    let dims: Vec<NdDimension> = items
360                        .iter()
361                        .filter_map(|item| {
362                            if let DecodedValue::Structure(fs) = item {
363                                Some(NdDimension {
364                                    size: fs.iter().find(|(n, _)| n == "size").and_then(|(_, v)| decoded_to_i32(v)).unwrap_or(0),
365                                    offset: fs.iter().find(|(n, _)| n == "offset").and_then(|(_, v)| decoded_to_i32(v)).unwrap_or(0),
366                                    full_size: fs.iter().find(|(n, _)| n == "fullSize").and_then(|(_, v)| decoded_to_i32(v)).unwrap_or(0),
367                                    binning: fs.iter().find(|(n, _)| n == "binning").and_then(|(_, v)| decoded_to_i32(v)).unwrap_or(1),
368                                    reverse: fs.iter().find(|(n, _)| n == "reverse").and_then(|(_, v)| decoded_to_bool(v)).unwrap_or(false),
369                                })
370                            } else {
371                                None
372                            }
373                        })
374                        .collect();
375                    if !dims.is_empty() && nt.dimension != dims {
376                        nt.dimension = dims;
377                        changed = true;
378                    }
379                }
380            }
381            "descriptor" => {
382                if let Some(s) = decoded_to_string(field_value) {
383                    let next = if s.is_empty() { None } else { Some(s) };
384                    if nt.descriptor != next {
385                        nt.descriptor = next;
386                        changed = true;
387                    }
388                }
389            }
390            "alarm" => {
391                if let Some(alarm) = decode_nt_alarm(field_value) {
392                    if nt.alarm.as_ref() != Some(&alarm) {
393                        nt.alarm = Some(alarm);
394                        changed = true;
395                    }
396                }
397            }
398            "timeStamp" => {
399                if let Some(ts) = decode_nt_timestamp(field_value) {
400                    if nt.time_stamp.as_ref() != Some(&ts) {
401                        nt.time_stamp = Some(ts);
402                        changed = true;
403                    }
404                }
405            }
406            "dataTimeStamp" => {
407                if let Some(ts) = decode_nt_timestamp(field_value) {
408                    if nt.data_time_stamp != ts {
409                        nt.data_time_stamp = ts;
410                        changed = true;
411                    }
412                }
413            }
414            "display" => {
415                if let Some(display) = decode_nt_display(field_value) {
416                    if nt.display.as_ref() != Some(&display) {
417                        nt.display = Some(display);
418                        changed = true;
419                    }
420                }
421            }
422            "attribute" => {
423                if let DecodedValue::Array(items) = field_value {
424                    let attrs: Vec<NtAttribute> = items
425                        .iter()
426                        .filter_map(|item| {
427                            if let DecodedValue::Structure(fs) = item {
428                                let attr_name = fs.iter().find(|(n, _)| n == "name").and_then(|(_, v)| decoded_to_string(v)).unwrap_or_default();
429                                let attr_value = fs.iter().find(|(n, _)| n == "value").map(|(_, v)| decoded_to_scalar_value(v)).unwrap_or(ScalarValue::I32(0));
430                                let descriptor = fs.iter().find(|(n, _)| n == "descriptor").and_then(|(_, v)| decoded_to_string(v)).unwrap_or_default();
431                                let source_type = fs.iter().find(|(n, _)| n == "sourceType").and_then(|(_, v)| decoded_to_i32(v)).unwrap_or(0);
432                                let source = fs.iter().find(|(n, _)| n == "source").and_then(|(_, v)| decoded_to_string(v)).unwrap_or_default();
433                                Some(NtAttribute {
434                                    name: attr_name,
435                                    value: attr_value,
436                                    descriptor,
437                                    source_type,
438                                    source,
439                                })
440                            } else {
441                                None
442                            }
443                        })
444                        .collect();
445                    if !attrs.is_empty() && nt.attribute != attrs {
446                        nt.attribute = attrs;
447                        changed = true;
448                    }
449                }
450            }
451            _ => {}
452        }
453    }
454    changed
455}