std_rs/device_support/
epid_fast.rs1use std::sync::{Arc, Mutex};
2use std::time::Instant;
3
4use epics_base_rs::error::CaResult;
5use epics_base_rs::server::device_support::{DeviceReadOutcome, DeviceSupport};
6use epics_base_rs::server::record::Record;
7
8use crate::records::epid::EpidRecord;
9
10pub struct EpidFastDeviceSupport {
38 pvt: Arc<Mutex<EpidFastPvt>>,
39}
40
41pub struct EpidFastPvt {
44 pub kp: f64,
46 pub ki: f64,
47 pub kd: f64,
48 pub drvh: f64,
49 pub drvl: f64,
50 pub val: f64, pub fbon: bool,
52 pub fmod: i16,
53
54 pub cval: f64,
56 pub oval: f64,
57 pub err: f64,
58 pub p: f64,
59 pub i: f64,
60 pub d: f64,
61 pub dt: f64,
62 pub ct: Instant,
63 pub fbop: bool,
64
65 pub num_average: u32,
67 pub accumulated: f64,
68 pub count: u32,
69
70 pub output_writer: Option<Arc<Mutex<dyn FnMut(f64) + Send>>>,
72}
73
74impl Default for EpidFastPvt {
75 fn default() -> Self {
76 let now = Instant::now();
77 Self {
78 kp: 0.0,
79 ki: 0.0,
80 kd: 0.0,
81 drvh: 0.0,
82 drvl: 0.0,
83 val: 0.0,
84 fbon: false,
85 fmod: 0,
86 cval: 0.0,
87 oval: 0.0,
88 err: 0.0,
89 p: 0.0,
90 i: 0.0,
91 d: 0.0,
92 dt: 0.0,
93 ct: now,
94 fbop: false,
95 num_average: 1,
96 accumulated: 0.0,
97 count: 0,
98 output_writer: None,
99 }
100 }
101}
102
103impl EpidFastPvt {
104 pub fn do_pid(&mut self, new_cval: f64) {
107 self.accumulated += new_cval;
109 self.count += 1;
110 if self.count < self.num_average {
111 return;
112 }
113 let cval = self.accumulated / self.count as f64;
114 self.accumulated = 0.0;
115 self.count = 0;
116
117 let pcval = self.cval;
118 self.cval = cval;
119
120 let ct = Instant::now();
121 let dt = ct.duration_since(self.ct).as_secs_f64();
122 self.ct = ct;
123 self.dt = dt;
124
125 let ep = self.err;
126 let mut oval = self.oval;
127
128 match self.fmod {
129 0 => {
130 let e = self.val - cval;
132 let de = e - ep;
133 self.p = self.kp * e;
134 let di = self.kp * self.ki * e * dt;
135
136 if self.fbon {
137 if !self.fbop {
138 self.i = self.oval;
139 } else {
140 if (oval > self.drvl && oval < self.drvh)
141 || (oval >= self.drvh && di < 0.0)
142 || (oval <= self.drvl && di > 0.0)
143 {
144 self.i += di;
145 self.i = self.i.clamp(self.drvl, self.drvh);
146 }
147 }
148 }
149 if self.ki == 0.0 {
150 self.i = 0.0;
151 }
152 self.d = if dt > 0.0 {
153 self.kp * self.kd * (de / dt)
154 } else {
155 0.0
156 };
157 self.err = e;
158 oval = self.p + self.i + self.d;
159 }
160 1 => {
161 if self.fbon {
163 if !self.fbop {
164 oval = self.oval;
165 } else {
166 let e = cval - pcval;
167 let sign = if self.d > 0.0 { 1.0 } else { -1.0 };
168 let sign = if (self.kp > 0.0 && e < 0.0) || (self.kp < 0.0 && e > 0.0) {
169 -sign
170 } else {
171 sign
172 };
173 self.d = self.kp * sign;
174 oval = self.oval + self.d;
175 }
176 }
177 }
178 _ => {}
179 }
180
181 oval = oval.clamp(self.drvl, self.drvh);
183 self.oval = oval;
184 self.fbop = self.fbon;
185
186 if self.fbon {
188 if let Some(ref writer) = self.output_writer {
189 if let Ok(mut w) = writer.lock() {
190 w(self.oval);
191 }
192 }
193 }
194 }
195}
196
197impl Default for EpidFastDeviceSupport {
198 fn default() -> Self {
199 Self::new()
200 }
201}
202
203impl EpidFastDeviceSupport {
204 pub fn new() -> Self {
205 Self {
206 pvt: Arc::new(Mutex::new(EpidFastPvt::default())),
207 }
208 }
209
210 pub fn pvt(&self) -> Arc<Mutex<EpidFastPvt>> {
212 Arc::clone(&self.pvt)
213 }
214
215 pub fn start_callback_loop(
224 &self,
225 mut input_rx: tokio::sync::mpsc::Receiver<f64>,
226 output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>,
227 ) {
228 let pvt = Arc::clone(&self.pvt);
229
230 {
232 let mut p = pvt.lock().unwrap();
233 p.output_writer = Some(output_fn);
234 }
235
236 tokio::spawn(async move {
237 while let Some(new_cval) = input_rx.recv().await {
238 let mut p = pvt.lock().unwrap();
239 p.do_pid(new_cval);
240 }
241 });
242 }
243
244 pub fn start_from_asyn_interrupts(
249 &self,
250 mut interrupt_rx: tokio::sync::broadcast::Receiver<asyn_rs::interrupt::InterruptValue>,
251 input_reason: usize,
252 output_fn: Arc<Mutex<dyn FnMut(f64) + Send>>,
253 ) {
254 let pvt = Arc::clone(&self.pvt);
255
256 {
257 let mut p = pvt.lock().unwrap();
258 p.output_writer = Some(output_fn);
259 }
260
261 tokio::spawn(async move {
262 loop {
263 match interrupt_rx.recv().await {
264 Ok(iv) => {
265 if iv.reason == input_reason {
266 let v = match &iv.value {
267 asyn_rs::param::ParamValue::Float64(f) => Some(*f),
268 asyn_rs::param::ParamValue::Int32(i) => Some(*i as f64),
269 asyn_rs::param::ParamValue::Int64(i) => Some(*i as f64),
270 _ => None,
271 };
272 if let Some(v) = v {
273 let mut p = pvt.lock().unwrap();
274 p.do_pid(v);
275 }
276 }
277 }
278 Err(tokio::sync::broadcast::error::RecvError::Closed) => break,
279 Err(tokio::sync::broadcast::error::RecvError::Lagged(_)) => {
280 }
282 }
283 }
284 });
285 }
286
287 fn update_params_from_record(&self, epid: &EpidRecord) {
289 let mut pvt = self.pvt.lock().unwrap();
290 pvt.kp = epid.kp;
291 pvt.ki = epid.ki;
292 pvt.kd = epid.kd;
293 pvt.drvh = epid.drvh;
294 pvt.drvl = epid.drvl;
295 pvt.val = epid.val;
296 pvt.fbon = epid.fbon != 0;
297 pvt.fmod = epid.fmod;
298 }
299
300 fn update_record_from_params(&self, epid: &mut EpidRecord) {
302 let pvt = self.pvt.lock().unwrap();
303 epid.cval = pvt.cval;
304 epid.oval = pvt.oval;
305 epid.err = pvt.err;
306 epid.p = pvt.p;
307 epid.i = pvt.i;
308 epid.d = pvt.d;
309 epid.dt = pvt.dt;
310 epid.fbop = if pvt.fbop { 1 } else { 0 };
311 }
312}
313
314impl DeviceSupport for EpidFastDeviceSupport {
315 fn dtyp(&self) -> &str {
316 "Fast Epid"
317 }
318
319 fn read(&mut self, record: &mut dyn Record) -> CaResult<DeviceReadOutcome> {
320 let epid = record
321 .as_any_mut()
322 .and_then(|a| a.downcast_mut::<EpidRecord>())
323 .expect("EpidFastDeviceSupport requires an EpidRecord");
324
325 self.update_params_from_record(epid);
327 self.update_record_from_params(epid);
329 Ok(DeviceReadOutcome::computed())
330 }
331
332 fn write(&mut self, _record: &mut dyn Record) -> CaResult<()> {
333 Ok(())
334 }
335}