1use std::any::Any;
2use std::time::Instant;
3
4use epics_base_rs::error::{CaError, CaResult};
5use epics_base_rs::server::record::{FieldDesc, ProcessOutcome, Record};
6use epics_base_rs::types::{DbFieldType, EpicsValue};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10#[repr(i16)]
11pub enum FeedbackMode {
12 #[default]
13 Pid = 0,
14 MaxMin = 1,
15}
16
17impl From<i16> for FeedbackMode {
18 fn from(v: i16) -> Self {
19 match v {
20 1 => FeedbackMode::MaxMin,
21 _ => FeedbackMode::Pid,
22 }
23 }
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28#[repr(i16)]
29pub enum FeedbackState {
30 #[default]
31 Off = 0,
32 On = 1,
33}
34
35impl From<i16> for FeedbackState {
36 fn from(v: i16) -> Self {
37 match v {
38 1 => FeedbackState::On,
39 _ => FeedbackState::Off,
40 }
41 }
42}
43
44pub struct EpidRecord {
50 pub val: f64,
53 pub smsl: i16,
55 pub stpl: String,
57 pub inp: String,
59 pub outl: String,
61 pub trig: String,
63 pub tval: f64,
65 pub cval: f64,
67 pub cvlp: f64,
69 pub oval: f64,
71 pub ovlp: f64,
73 pub kp: f64,
75 pub ki: f64,
77 pub kd: f64,
79 pub p: f64,
81 pub pp: f64,
83 pub i: f64,
85 pub ip: f64,
87 pub d: f64,
89 pub dp: f64,
91 pub err: f64,
93 pub errp: f64,
95 pub dt: f64,
97 pub dtp: f64,
99 pub mdt: f64,
101 pub fmod: i16,
103 pub fbon: i16,
105 pub fbop: i16,
107 pub odel: f64,
109
110 pub prec: i16,
113 pub egu: String,
115 pub hopr: f64,
117 pub lopr: f64,
119 pub drvh: f64,
121 pub drvl: f64,
123
124 pub hihi: f64,
127 pub lolo: f64,
129 pub high: f64,
131 pub low: f64,
133 pub hhsv: i16,
135 pub llsv: i16,
137 pub hsv: i16,
139 pub lsv: i16,
141 pub hyst: f64,
143 pub lalm: f64,
145
146 pub adel: f64,
149 pub mdel: f64,
151 pub alst: f64,
153 pub mlst: f64,
155
156 pub(crate) ct: Instant,
159 #[allow(dead_code)]
161 pub(crate) ctp: Instant,
162
163 device_did_compute: bool,
168}
169
170impl Default for EpidRecord {
171 fn default() -> Self {
172 let now = Instant::now();
173 Self {
174 val: 0.0,
175 smsl: 0,
176 stpl: String::new(),
177 inp: String::new(),
178 outl: String::new(),
179 trig: String::new(),
180 tval: 0.0,
181 cval: 0.0,
182 cvlp: 0.0,
183 oval: 0.0,
184 ovlp: 0.0,
185 kp: 0.0,
186 ki: 0.0,
187 kd: 0.0,
188 p: 0.0,
189 pp: 0.0,
190 i: 0.0,
191 ip: 0.0,
192 d: 0.0,
193 dp: 0.0,
194 err: 0.0,
195 errp: 0.0,
196 dt: 0.0,
197 dtp: 0.0,
198 mdt: 0.0,
199 fmod: 0,
200 fbon: 0,
201 fbop: 0,
202 odel: 0.0,
203 prec: 0,
204 egu: String::new(),
205 hopr: 0.0,
206 lopr: 0.0,
207 drvh: 0.0,
208 drvl: 0.0,
209 hihi: 0.0,
210 lolo: 0.0,
211 high: 0.0,
212 low: 0.0,
213 hhsv: 0,
214 llsv: 0,
215 hsv: 0,
216 lsv: 0,
217 hyst: 0.0,
218 lalm: 0.0,
219 adel: 0.0,
220 mdel: 0.0,
221 alst: 0.0,
222 mlst: 0.0,
223 ct: now,
224 ctp: now,
225 device_did_compute: false,
226 }
227 }
228}
229
230impl EpidRecord {
231 pub fn check_alarms(&mut self) -> Option<(u16, u16)> {
234 let val = self.val;
235 let hyst = self.hyst;
236 let lalm = self.lalm;
237
238 if self.hhsv != 0 {
240 if val >= self.hihi || (lalm == self.hihi && val >= self.hihi - hyst) {
241 self.lalm = self.hihi;
242 return Some((3, self.hhsv as u16)); }
244 }
245
246 if self.llsv != 0 {
248 if val <= self.lolo || (lalm == self.lolo && val <= self.lolo + hyst) {
249 self.lalm = self.lolo;
250 return Some((4, self.llsv as u16)); }
252 }
253
254 if self.hsv != 0 {
256 if val >= self.high || (lalm == self.high && val >= self.high - hyst) {
257 self.lalm = self.high;
258 return Some((1, self.hsv as u16)); }
260 }
261
262 if self.lsv != 0 {
264 if val <= self.low || (lalm == self.low && val <= self.low + hyst) {
265 self.lalm = self.low;
266 return Some((2, self.lsv as u16)); }
268 }
269
270 self.lalm = val;
272 None
273 }
274
275 pub fn update_monitors(&mut self) {
278 self.ovlp = self.oval;
280 self.pp = self.p;
281 self.ip = self.i;
282 self.dp = self.d;
283 self.dtp = self.dt;
284 self.errp = self.err;
285 self.cvlp = self.cval;
286
287 if self.mdel == 0.0 || (self.mlst - self.val).abs() > self.mdel {
289 self.mlst = self.val;
290 }
291 if self.adel == 0.0 || (self.alst - self.val).abs() > self.adel {
292 self.alst = self.val;
293 }
294 }
295}
296
297static FIELDS: &[FieldDesc] = &[
298 FieldDesc {
300 name: "VAL",
301 dbf_type: DbFieldType::Double,
302 read_only: false,
303 },
304 FieldDesc {
305 name: "SMSL",
306 dbf_type: DbFieldType::Short,
307 read_only: false,
308 },
309 FieldDesc {
310 name: "STPL",
311 dbf_type: DbFieldType::String,
312 read_only: false,
313 },
314 FieldDesc {
315 name: "INP",
316 dbf_type: DbFieldType::String,
317 read_only: false,
318 },
319 FieldDesc {
320 name: "OUTL",
321 dbf_type: DbFieldType::String,
322 read_only: false,
323 },
324 FieldDesc {
325 name: "TRIG",
326 dbf_type: DbFieldType::String,
327 read_only: false,
328 },
329 FieldDesc {
330 name: "TVAL",
331 dbf_type: DbFieldType::Double,
332 read_only: false,
333 },
334 FieldDesc {
335 name: "CVAL",
336 dbf_type: DbFieldType::Double,
337 read_only: true,
338 },
339 FieldDesc {
340 name: "CVLP",
341 dbf_type: DbFieldType::Double,
342 read_only: true,
343 },
344 FieldDesc {
345 name: "OVAL",
346 dbf_type: DbFieldType::Double,
347 read_only: true,
348 },
349 FieldDesc {
350 name: "OVLP",
351 dbf_type: DbFieldType::Double,
352 read_only: true,
353 },
354 FieldDesc {
355 name: "KP",
356 dbf_type: DbFieldType::Double,
357 read_only: false,
358 },
359 FieldDesc {
360 name: "KI",
361 dbf_type: DbFieldType::Double,
362 read_only: false,
363 },
364 FieldDesc {
365 name: "KD",
366 dbf_type: DbFieldType::Double,
367 read_only: false,
368 },
369 FieldDesc {
370 name: "P",
371 dbf_type: DbFieldType::Double,
372 read_only: true,
373 },
374 FieldDesc {
375 name: "PP",
376 dbf_type: DbFieldType::Double,
377 read_only: true,
378 },
379 FieldDesc {
380 name: "I",
381 dbf_type: DbFieldType::Double,
382 read_only: false,
383 },
384 FieldDesc {
385 name: "IP",
386 dbf_type: DbFieldType::Double,
387 read_only: true,
388 },
389 FieldDesc {
390 name: "D",
391 dbf_type: DbFieldType::Double,
392 read_only: true,
393 },
394 FieldDesc {
395 name: "DP",
396 dbf_type: DbFieldType::Double,
397 read_only: true,
398 },
399 FieldDesc {
400 name: "ERR",
401 dbf_type: DbFieldType::Double,
402 read_only: true,
403 },
404 FieldDesc {
405 name: "ERRP",
406 dbf_type: DbFieldType::Double,
407 read_only: true,
408 },
409 FieldDesc {
410 name: "DT",
411 dbf_type: DbFieldType::Double,
412 read_only: false,
413 },
414 FieldDesc {
415 name: "DTP",
416 dbf_type: DbFieldType::Double,
417 read_only: true,
418 },
419 FieldDesc {
420 name: "MDT",
421 dbf_type: DbFieldType::Double,
422 read_only: false,
423 },
424 FieldDesc {
425 name: "FMOD",
426 dbf_type: DbFieldType::Short,
427 read_only: false,
428 },
429 FieldDesc {
430 name: "FBON",
431 dbf_type: DbFieldType::Short,
432 read_only: false,
433 },
434 FieldDesc {
435 name: "FBOP",
436 dbf_type: DbFieldType::Short,
437 read_only: true,
438 },
439 FieldDesc {
440 name: "ODEL",
441 dbf_type: DbFieldType::Double,
442 read_only: false,
443 },
444 FieldDesc {
446 name: "PREC",
447 dbf_type: DbFieldType::Short,
448 read_only: false,
449 },
450 FieldDesc {
451 name: "EGU",
452 dbf_type: DbFieldType::String,
453 read_only: false,
454 },
455 FieldDesc {
456 name: "HOPR",
457 dbf_type: DbFieldType::Double,
458 read_only: false,
459 },
460 FieldDesc {
461 name: "LOPR",
462 dbf_type: DbFieldType::Double,
463 read_only: false,
464 },
465 FieldDesc {
466 name: "DRVH",
467 dbf_type: DbFieldType::Double,
468 read_only: false,
469 },
470 FieldDesc {
471 name: "DRVL",
472 dbf_type: DbFieldType::Double,
473 read_only: false,
474 },
475 FieldDesc {
477 name: "HIHI",
478 dbf_type: DbFieldType::Double,
479 read_only: false,
480 },
481 FieldDesc {
482 name: "LOLO",
483 dbf_type: DbFieldType::Double,
484 read_only: false,
485 },
486 FieldDesc {
487 name: "HIGH",
488 dbf_type: DbFieldType::Double,
489 read_only: false,
490 },
491 FieldDesc {
492 name: "LOW",
493 dbf_type: DbFieldType::Double,
494 read_only: false,
495 },
496 FieldDesc {
497 name: "HHSV",
498 dbf_type: DbFieldType::Short,
499 read_only: false,
500 },
501 FieldDesc {
502 name: "LLSV",
503 dbf_type: DbFieldType::Short,
504 read_only: false,
505 },
506 FieldDesc {
507 name: "HSV",
508 dbf_type: DbFieldType::Short,
509 read_only: false,
510 },
511 FieldDesc {
512 name: "LSV",
513 dbf_type: DbFieldType::Short,
514 read_only: false,
515 },
516 FieldDesc {
517 name: "HYST",
518 dbf_type: DbFieldType::Double,
519 read_only: false,
520 },
521 FieldDesc {
522 name: "LALM",
523 dbf_type: DbFieldType::Double,
524 read_only: true,
525 },
526 FieldDesc {
528 name: "ADEL",
529 dbf_type: DbFieldType::Double,
530 read_only: false,
531 },
532 FieldDesc {
533 name: "MDEL",
534 dbf_type: DbFieldType::Double,
535 read_only: false,
536 },
537 FieldDesc {
538 name: "ALST",
539 dbf_type: DbFieldType::Double,
540 read_only: true,
541 },
542 FieldDesc {
543 name: "MLST",
544 dbf_type: DbFieldType::Double,
545 read_only: true,
546 },
547];
548
549impl Record for EpidRecord {
550 fn record_type(&self) -> &'static str {
551 "epid"
552 }
553
554 fn process(&mut self) -> CaResult<ProcessOutcome> {
555 if !self.device_did_compute {
568 crate::device_support::epid_soft::EpidSoftDeviceSupport::do_pid(self);
569 }
570 self.device_did_compute = false; self.check_alarms();
573 self.update_monitors();
574
575 let actions = Vec::new();
577 Ok(ProcessOutcome::complete_with(actions))
578 }
579
580 fn get_field(&self, name: &str) -> Option<EpicsValue> {
581 match name {
582 "VAL" => Some(EpicsValue::Double(self.val)),
583 "SMSL" => Some(EpicsValue::Short(self.smsl)),
584 "STPL" => Some(EpicsValue::String(self.stpl.clone())),
585 "INP" => Some(EpicsValue::String(self.inp.clone())),
586 "OUTL" => Some(EpicsValue::String(self.outl.clone())),
587 "TRIG" => Some(EpicsValue::String(self.trig.clone())),
588 "TVAL" => Some(EpicsValue::Double(self.tval)),
589 "CVAL" => Some(EpicsValue::Double(self.cval)),
590 "CVLP" => Some(EpicsValue::Double(self.cvlp)),
591 "OVAL" => Some(EpicsValue::Double(self.oval)),
592 "OVLP" => Some(EpicsValue::Double(self.ovlp)),
593 "KP" => Some(EpicsValue::Double(self.kp)),
594 "KI" => Some(EpicsValue::Double(self.ki)),
595 "KD" => Some(EpicsValue::Double(self.kd)),
596 "P" => Some(EpicsValue::Double(self.p)),
597 "PP" => Some(EpicsValue::Double(self.pp)),
598 "I" => Some(EpicsValue::Double(self.i)),
599 "IP" => Some(EpicsValue::Double(self.ip)),
600 "D" => Some(EpicsValue::Double(self.d)),
601 "DP" => Some(EpicsValue::Double(self.dp)),
602 "ERR" => Some(EpicsValue::Double(self.err)),
603 "ERRP" => Some(EpicsValue::Double(self.errp)),
604 "DT" => Some(EpicsValue::Double(self.dt)),
605 "DTP" => Some(EpicsValue::Double(self.dtp)),
606 "MDT" => Some(EpicsValue::Double(self.mdt)),
607 "FMOD" => Some(EpicsValue::Short(self.fmod)),
608 "FBON" => Some(EpicsValue::Short(self.fbon)),
609 "FBOP" => Some(EpicsValue::Short(self.fbop)),
610 "ODEL" => Some(EpicsValue::Double(self.odel)),
611 "PREC" => Some(EpicsValue::Short(self.prec)),
612 "EGU" => Some(EpicsValue::String(self.egu.clone())),
613 "HOPR" => Some(EpicsValue::Double(self.hopr)),
614 "LOPR" => Some(EpicsValue::Double(self.lopr)),
615 "DRVH" => Some(EpicsValue::Double(self.drvh)),
616 "DRVL" => Some(EpicsValue::Double(self.drvl)),
617 "HIHI" => Some(EpicsValue::Double(self.hihi)),
618 "LOLO" => Some(EpicsValue::Double(self.lolo)),
619 "HIGH" => Some(EpicsValue::Double(self.high)),
620 "LOW" => Some(EpicsValue::Double(self.low)),
621 "HHSV" => Some(EpicsValue::Short(self.hhsv)),
622 "LLSV" => Some(EpicsValue::Short(self.llsv)),
623 "HSV" => Some(EpicsValue::Short(self.hsv)),
624 "LSV" => Some(EpicsValue::Short(self.lsv)),
625 "HYST" => Some(EpicsValue::Double(self.hyst)),
626 "LALM" => Some(EpicsValue::Double(self.lalm)),
627 "ADEL" => Some(EpicsValue::Double(self.adel)),
628 "MDEL" => Some(EpicsValue::Double(self.mdel)),
629 "ALST" => Some(EpicsValue::Double(self.alst)),
630 "MLST" => Some(EpicsValue::Double(self.mlst)),
631 _ => None,
632 }
633 }
634
635 fn put_field(&mut self, name: &str, value: EpicsValue) -> CaResult<()> {
636 match name {
637 "VAL" => match value {
638 EpicsValue::Double(v) => {
639 self.val = v;
640 Ok(())
641 }
642 _ => Err(CaError::TypeMismatch(name.into())),
643 },
644 "SMSL" => match value {
645 EpicsValue::Short(v) => {
646 self.smsl = v;
647 Ok(())
648 }
649 _ => Err(CaError::TypeMismatch(name.into())),
650 },
651 "STPL" => match value {
652 EpicsValue::String(v) => {
653 self.stpl = v;
654 Ok(())
655 }
656 _ => Err(CaError::TypeMismatch(name.into())),
657 },
658 "INP" => match value {
659 EpicsValue::String(v) => {
660 self.inp = v;
661 Ok(())
662 }
663 _ => Err(CaError::TypeMismatch(name.into())),
664 },
665 "OUTL" => match value {
666 EpicsValue::String(v) => {
667 self.outl = v;
668 Ok(())
669 }
670 _ => Err(CaError::TypeMismatch(name.into())),
671 },
672 "TRIG" => match value {
673 EpicsValue::String(v) => {
674 self.trig = v;
675 Ok(())
676 }
677 _ => Err(CaError::TypeMismatch(name.into())),
678 },
679 "TVAL" => match value {
680 EpicsValue::Double(v) => {
681 self.tval = v;
682 Ok(())
683 }
684 _ => Err(CaError::TypeMismatch(name.into())),
685 },
686 "KP" => match value {
687 EpicsValue::Double(v) => {
688 self.kp = v;
689 Ok(())
690 }
691 _ => Err(CaError::TypeMismatch(name.into())),
692 },
693 "KI" => match value {
694 EpicsValue::Double(v) => {
695 self.ki = v;
696 Ok(())
697 }
698 _ => Err(CaError::TypeMismatch(name.into())),
699 },
700 "KD" => match value {
701 EpicsValue::Double(v) => {
702 self.kd = v;
703 Ok(())
704 }
705 _ => Err(CaError::TypeMismatch(name.into())),
706 },
707 "I" => match value {
708 EpicsValue::Double(v) => {
709 self.i = v;
710 Ok(())
711 }
712 _ => Err(CaError::TypeMismatch(name.into())),
713 },
714 "IP" => match value {
715 EpicsValue::Double(v) => {
716 self.ip = v;
717 Ok(())
718 }
719 _ => Err(CaError::TypeMismatch(name.into())),
720 },
721 "DT" => match value {
722 EpicsValue::Double(v) => {
723 self.dt = v;
724 Ok(())
725 }
726 _ => Err(CaError::TypeMismatch(name.into())),
727 },
728 "MDT" => match value {
729 EpicsValue::Double(v) => {
730 self.mdt = v;
731 Ok(())
732 }
733 _ => Err(CaError::TypeMismatch(name.into())),
734 },
735 "FMOD" => match value {
736 EpicsValue::Short(v) => {
737 self.fmod = v;
738 Ok(())
739 }
740 _ => Err(CaError::TypeMismatch(name.into())),
741 },
742 "FBON" => match value {
743 EpicsValue::Short(v) => {
744 self.fbon = v;
745 Ok(())
746 }
747 _ => Err(CaError::TypeMismatch(name.into())),
748 },
749 "ODEL" => match value {
750 EpicsValue::Double(v) => {
751 self.odel = v;
752 Ok(())
753 }
754 _ => Err(CaError::TypeMismatch(name.into())),
755 },
756 "PREC" => match value {
757 EpicsValue::Short(v) => {
758 self.prec = v;
759 Ok(())
760 }
761 _ => Err(CaError::TypeMismatch(name.into())),
762 },
763 "EGU" => match value {
764 EpicsValue::String(v) => {
765 self.egu = v;
766 Ok(())
767 }
768 _ => Err(CaError::TypeMismatch(name.into())),
769 },
770 "HOPR" => match value {
771 EpicsValue::Double(v) => {
772 self.hopr = v;
773 Ok(())
774 }
775 _ => Err(CaError::TypeMismatch(name.into())),
776 },
777 "LOPR" => match value {
778 EpicsValue::Double(v) => {
779 self.lopr = v;
780 Ok(())
781 }
782 _ => Err(CaError::TypeMismatch(name.into())),
783 },
784 "DRVH" => match value {
785 EpicsValue::Double(v) => {
786 self.drvh = v;
787 Ok(())
788 }
789 _ => Err(CaError::TypeMismatch(name.into())),
790 },
791 "DRVL" => match value {
792 EpicsValue::Double(v) => {
793 self.drvl = v;
794 Ok(())
795 }
796 _ => Err(CaError::TypeMismatch(name.into())),
797 },
798 "HIHI" => match value {
799 EpicsValue::Double(v) => {
800 self.hihi = v;
801 Ok(())
802 }
803 _ => Err(CaError::TypeMismatch(name.into())),
804 },
805 "LOLO" => match value {
806 EpicsValue::Double(v) => {
807 self.lolo = v;
808 Ok(())
809 }
810 _ => Err(CaError::TypeMismatch(name.into())),
811 },
812 "HIGH" => match value {
813 EpicsValue::Double(v) => {
814 self.high = v;
815 Ok(())
816 }
817 _ => Err(CaError::TypeMismatch(name.into())),
818 },
819 "LOW" => match value {
820 EpicsValue::Double(v) => {
821 self.low = v;
822 Ok(())
823 }
824 _ => Err(CaError::TypeMismatch(name.into())),
825 },
826 "HHSV" => match value {
827 EpicsValue::Short(v) => {
828 self.hhsv = v;
829 Ok(())
830 }
831 _ => Err(CaError::TypeMismatch(name.into())),
832 },
833 "LLSV" => match value {
834 EpicsValue::Short(v) => {
835 self.llsv = v;
836 Ok(())
837 }
838 _ => Err(CaError::TypeMismatch(name.into())),
839 },
840 "HSV" => match value {
841 EpicsValue::Short(v) => {
842 self.hsv = v;
843 Ok(())
844 }
845 _ => Err(CaError::TypeMismatch(name.into())),
846 },
847 "LSV" => match value {
848 EpicsValue::Short(v) => {
849 self.lsv = v;
850 Ok(())
851 }
852 _ => Err(CaError::TypeMismatch(name.into())),
853 },
854 "HYST" => match value {
855 EpicsValue::Double(v) => {
856 self.hyst = v;
857 Ok(())
858 }
859 _ => Err(CaError::TypeMismatch(name.into())),
860 },
861 "ADEL" => match value {
862 EpicsValue::Double(v) => {
863 self.adel = v;
864 Ok(())
865 }
866 _ => Err(CaError::TypeMismatch(name.into())),
867 },
868 "MDEL" => match value {
869 EpicsValue::Double(v) => {
870 self.mdel = v;
871 Ok(())
872 }
873 _ => Err(CaError::TypeMismatch(name.into())),
874 },
875 "CVAL" | "CVLP" | "OVAL" | "OVLP" | "P" | "PP" | "D" | "DP" | "ERR" | "ERRP"
877 | "DTP" | "FBOP" | "LALM" | "ALST" | "MLST" => Err(CaError::ReadOnlyField(name.into())),
878 _ => Err(CaError::FieldNotFound(name.into())),
879 }
880 }
881
882 fn field_list(&self) -> &'static [FieldDesc] {
883 FIELDS
884 }
885
886 fn as_any_mut(&mut self) -> Option<&mut dyn Any> {
887 Some(self)
888 }
889
890 fn set_device_did_compute(&mut self, did_compute: bool) {
891 self.device_did_compute = did_compute;
892 }
893
894 fn put_field_internal(
895 &mut self,
896 name: &str,
897 value: EpicsValue,
898 ) -> epics_base_rs::error::CaResult<()> {
899 match name {
902 "CVAL" => match value {
903 EpicsValue::Double(v) => {
904 self.cval = v;
905 Ok(())
906 }
907 _ => Err(CaError::TypeMismatch(name.into())),
908 },
909 "OVAL" => match value {
910 EpicsValue::Double(v) => {
911 self.oval = v;
912 Ok(())
913 }
914 _ => Err(CaError::TypeMismatch(name.into())),
915 },
916 "P" => match value {
917 EpicsValue::Double(v) => {
918 self.p = v;
919 Ok(())
920 }
921 _ => Err(CaError::TypeMismatch(name.into())),
922 },
923 "D" => match value {
924 EpicsValue::Double(v) => {
925 self.d = v;
926 Ok(())
927 }
928 _ => Err(CaError::TypeMismatch(name.into())),
929 },
930 "ERR" => match value {
931 EpicsValue::Double(v) => {
932 self.err = v;
933 Ok(())
934 }
935 _ => Err(CaError::TypeMismatch(name.into())),
936 },
937 _ => self.put_field(name, value),
938 }
939 }
940
941 fn multi_input_links(&self) -> &[(&'static str, &'static str)] {
942 if self.smsl == 1 {
947 static WITH_STPL: &[(&str, &str)] = &[("STPL", "VAL"), ("INP", "CVAL")];
949 WITH_STPL
950 } else {
951 static WITHOUT_STPL: &[(&str, &str)] = &[("INP", "CVAL")];
953 WITHOUT_STPL
954 }
955 }
956
957 fn multi_output_links(&self) -> &[(&'static str, &'static str)] {
958 static LINKS: &[(&str, &str)] = &[("OUTL", "OVAL")];
960 LINKS
961 }
962}