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