mrfioc2  2.3.0
seqconst.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2012 Brookhaven Science Associates, as Operator of
3 * Brookhaven National Laboratory.
4 * mrfioc2 is distributed subject to a Software License Agreement found
5 * in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7 /*
8  * Author: Michael Davidsaver <mdavidsaver@gmail.com>
9  */
10 
11 #include <math.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include <dbDefs.h>
16 #include <errlog.h>
17 #include <recGbl.h>
18 #include <alarm.h>
19 
20 #include <registryFunction.h>
21 
22 #include <menuFtype.h>
23 #include <aSubRecord.h>
24 
25 
26 #define NINPUTS (aSubRecordU - aSubRecordA)
27 
28 int seqConstDebug = 0;
29 
30 static
31 menuFtype seq_repeat_ft[] = {
32  menuFtypeULONG, menuFtypeULONG, menuFtypeULONG, menuFtypeDOUBLE, menuFtypeUCHAR
33 };
34 
35 static
36 menuFtype seq_repeat_ftv[] = {
37  menuFtypeDOUBLE, menuFtypeDOUBLE, menuFtypeUCHAR
38 };
39 
63 long seq_repeat(aSubRecord *prec)
64 {
65  unsigned int N,i;
66 
67  /* Inputs */
68  epicsUInt32 period;
69  epicsUInt32 num_cycles, per_mask, len_in, len_out, num_out;
70  double add;
71  double *cycle_D = prec->d;
72  epicsUInt8 *cycle_C = prec->e;
73 
74  /* Outputs */
75  double *period_D = prec->valb;
76  epicsUInt8 *period_C = prec->valc;
77 
78  if(prec->nsev>=INVALID_ALARM) /* Invalid inputs */
79  return -1;
80 
81  /* Check type and length of inputs and outputs */
82 
83  for(i=0; i<NELEMENTS(seq_repeat_ft); i++) {
84  if((&prec->fta)[i]!=seq_repeat_ft[i]) {
85  epicsPrintf("%s: Invalid type for FT%c\n", prec->name, 'A'+i);
86  goto fail;
87  }
88  if((&prec->nea)[i]==0) {
89  epicsPrintf("%s.NE%c empty\n", prec->name, 'A'+i);
90  goto fail;
91  }
92  }
93 
94  for(i=0; i<NELEMENTS(seq_repeat_ftv); i++) {
95  if((&prec->ftva)[i]!=seq_repeat_ftv[i]) {
96  epicsPrintf("%s: Invalid type for FTV%c\n", prec->name, 'A'+i);
97  goto fail;
98  }
99  if((&prec->nova)[i]==0) {
100  epicsPrintf("%s.NOV%c empty\n", prec->name, 'A'+i);
101  goto fail;
102  }
103  }
104 
105  period = *(epicsUInt32*)prec->a;
106  num_cycles = *(epicsUInt32*)prec->b;
107  per_mask = *(epicsUInt32*)prec->c;
108 
109  len_in = prec->ned;
110  if(len_in > prec->nee)
111  len_in = prec->nee;
112 
113  len_out = prec->novb;
114  if(len_out > prec->novc)
115  len_out = prec->novc;
116 
117  if(num_cycles==0) {
118  goto fail;
119  } else if(num_cycles>32) {
120  epicsPrintf("%s: Num cycles is out of range", prec->name);
121  goto fail;
122  }
123 
124  /* amount to add per cycle */
125  add = period/num_cycles;
126 
127  if(len_in * num_cycles > len_out) {
128  epicsPrintf("%s: Not enough elements for result. Have %u, need %u\n",
129  prec->name, len_out, len_in * num_cycles);
130  goto fail;
131  }
132 
133  if( period%num_cycles ) {
134  epicsPrintf("%s: %u cycles does not evenly divide period %u", prec->name, num_cycles, period);
135  goto fail;
136  }
137 
138  *(double*)prec->vala = add;
139 
140  num_out = 0;
141  for(N=0; per_mask && N<num_cycles; N++, per_mask>>=1) {
142  if(!(per_mask&1))
143  continue;
144  for(i=0; i<len_in; i++) {
145  if(cycle_D[i]>=add) {
146  len_in = i; /* truncate */
147  break;
148  }
149  *period_D++ = cycle_D[i] + add*N;
150  *period_C++ = cycle_C[i];
151  }
152  num_out++;
153  }
154 
155  if(num_out==0 || len_in==0) {
156  /* 0 length arrays aren't handled so well, so have 1 length w/ 0 value */
157  *period_D++ = 0.0;
158  *period_C++ = 0;
159  prec->nevb = prec->nevc = 1;
160  } else {
161  prec->nevb = prec->nevc = num_out * len_in;
162  }
163 
164  return 0;
165 
166 fail:
167  recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
168  return -1;
169 }
170 
190 long seq_merge(aSubRecord *prec)
191 {
192  unsigned int i;
193  epicsUInt32 in_pos[NINPUTS/2]; // position in each pair of input times/codes
194  unsigned int ninputs;
195 
196  epicsUInt32 maxout = prec->nova;
197  double *out_T = prec->vala;
198  epicsUInt8 *out_C = prec->valb;
199 
200  if(prec->nsev>=INVALID_ALARM) /* Invalid inputs */
201  return -1;
202 
203  memset(in_pos, 0, sizeof(in_pos));
204 
205  if(maxout > prec->novb)
206  maxout = prec->novb;
207 
208  /* check output types */
209  if(prec->ftva!=menuFtypeDOUBLE || prec->ftvb!=menuFtypeUCHAR ||
210  prec->nova==0 || prec->novb==0)
211  {
212  epicsPrintf("%s: Invalid types for lengths for VALA and/or VALB\n", prec->name);
213  goto alarm;
214  }
215 
216  {
217  unsigned int N;
218  for(N=0; N<NINPUTS/2; N++) {
219  if((&prec->fta)[2*N]!=menuFtypeDOUBLE)
220  break;
221  if((&prec->fta)[2*N+1]!=menuFtypeUCHAR)
222  break;
223 
224  /* Fail unless lengths match */
225  if((&prec->nea)[2*N]!=(&prec->nea)[2*N+1]) {
226  epicsPrintf("%s: NE%c (%d) != NE%c (%d)\n",
227  prec->name,
228  (char)('A'+2*N), (&prec->nea)[2*N],
229  (char)('A'+2*N+1), (&prec->nea)[2*N+1]);
230  goto fail;
231  }
232  }
233 
234  ninputs = N;
235  }
236 
237  if(ninputs==0) {
238  epicsPrintf("%s: No inputs configured!\n", prec->name);
239  goto fail;
240  }
241 
242  if(seqConstDebug>1) {
243  printf("%s Merge\n", prec->name);
244  }
245 
246  for(i=0; i<maxout; i++) {
247  unsigned int N;
248  int found_element = -1;
249  double last_time = -1.0; /* Initial value not used, but silences warning */
250 
251  for(N=0; N<ninputs; N++) {
252  double *T;
253  epicsUInt8 *C;
254 
255  T = (double*)(&prec->a)[2*N];
256  C = (epicsUInt8*)(&prec->a)[2*N+1];
257 
258  while(in_pos[N]<(&prec->nea)[2*N] && C[in_pos[N]]==0)
259  in_pos[N]++; /* skip entries with code 0 */
260 
261  if(in_pos[N]==(&prec->nea)[2*N])
262  continue; /* This input completely consumed */
263 
264  if(found_element==-1 || T[in_pos[N]]<last_time) {
265  found_element = N;
266  out_T[i] = T[in_pos[N]];
267  out_C[i] = C[in_pos[N]];
268  last_time = out_T[i];
269 
270  } else if(T[in_pos[N]]==last_time && found_element!=-1) {
271  /* Duplicate timestamp! */
272  printf("%s: Dup timestamp. %c[%u] and %c[%u]\n",
273  prec->name,
274  'A'+(2*found_element), in_pos[found_element]-1,
275  'A'+(2*N), in_pos[N]);
276  printf(" Found element: %d\n", found_element);
277  printf(" i=%u, N=%u, ninputs=%u\n", i, N, ninputs);
278  printf(" Out %u C=%u T=%f [", i, out_C[i], out_T[i]);
279  for(N=0; N<ninputs; N++)
280  printf("%u, ", in_pos[N/2]);
281  printf("]\n");
282  goto fail;
283  }
284  }
285 
286  if(found_element==-1) {
287  /* All inputs consumed */
288  goto done;
289  } else if(i>0 && out_T[i] < out_T[i-1]) {
290  epicsPrintf("%s: input %c times not sorted!\n", prec->name, 'A'+(2*found_element));
291  goto fail;
292  } else {
293  in_pos[found_element]++;
294  }
295 
296  if(seqConstDebug>1) {
297  printf("Out %u C=%u T=%f [", i, out_C[i], out_T[i]);
298  for(N=0; N<ninputs; N++)
299  printf("%u, ", in_pos[N/2]);
300  printf("\n");
301  }
302  }
303 
304  if(seqConstDebug>0) {
305  epicsPrintf("%s: merge result has %u element\n", prec->name, i);
306  }
307 
308  {
309  unsigned int N;
310  unsigned int nogood = 0;
311  for(N=0; N<ninputs; N++) {
312  if(in_pos[N]!=(&prec->nea)[2*N]) {
313  epicsPrintf("%s.%c: Not completely consumed! %u of %u\n",
314  prec->name, 'A'+(2*N), in_pos[N], (&prec->nea)[2*N]);
315  nogood = 1;
316  }
317  }
318  if(nogood)
319  goto fail;
320  }
321 
322 done:
323  if(i==0) {
324  if(seqConstDebug>0)
325  epicsPrintf("%s: merged yields empty sequence\n", prec->name);
326  /* result is really empty */
327  out_T[0] = 0.0;
328  out_C[0] = 0;
329  i = 1;
330  }
331  prec->neva = i;
332  prec->nevb = i;
333 
334  return 0;
335 
336 fail:
337  /* when possible ensure a sane output (do nothing)
338  * in case the alarm is ignored.
339  */
340  out_T[0] = 0.0;
341  out_C[0] = 0;
342  prec->neva = 1;
343  prec->nevb = 1;
344 alarm:
345  recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
346  return -1;
347 }
348 
362 long seq_shift(aSubRecord *prec)
363 {
364  double delay;
365  const double *input=(const double*)prec->b;
366  double *output=(double*)prec->vala;
367  epicsUInt32 i;
368 
369  if(prec->fta!=menuFtypeDOUBLE
370  || prec->ftb!=menuFtypeDOUBLE
371  || prec->ftva!=menuFtypeDOUBLE)
372  {
373  epicsPrintf("%s: invalid types\n", prec->name);
374  goto fail;
375  } else if(prec->nea==0 || prec->neb > prec->nova) {
376  epicsPrintf("%s: incorrect sizes\n", prec->name);
377  goto fail;
378  }
379 
380  delay = *(double*)prec->a;
381 
382  for(i=0; i<prec->neb; i++)
383  output[i] = input[i]+delay;
384 
385  prec->neva = prec->neb;
386 
387  return 0;
388 
389 fail:
390  recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM);
391  return -1;
392 }
393 
407 long seq_mask(aSubRecord *prec)
408 {
409  const epicsUInt8 *inp = (const epicsUInt8*)prec->a;
410  const epicsUInt32 *mask = (const epicsUInt32*)prec->b;
411  epicsUInt8 *out = (epicsUInt8*)prec->vala;
412  epicsUInt32 i, num = prec->neb*32;
413 
414  if(num>prec->nea)
415  num = prec->nea;
416  if(num>prec->nova)
417  num = prec->nova;
418 
419  for(i=0; i<num; i++) {
420  epicsUInt8 M = (mask[i/32]>>(i%32))&0x1;
421  out[i] = M ? inp[i] : 0;
422  }
423 
424  prec->neva = num;
425 
426  return 0;
427 }
428 
429 static registryFunctionRef asub_seq[] = {
430  {"Seq Repeat", (REGISTRYFUNCTION) seq_repeat},
431  {"Seq Merge", (REGISTRYFUNCTION) seq_merge},
432  {"Seq Shift", (REGISTRYFUNCTION) seq_shift},
433  {"Seq Mask", (REGISTRYFUNCTION) seq_mask},
434 };
435 
436 static
437 void asub_evg(void) {
438  registryFunctionRefAdd(asub_seq, NELEMENTS(asub_seq));
439 }
440 
441 #include <epicsExport.h>
442 
443 epicsExportRegistrar(asub_evg);
long seq_merge(aSubRecord *prec)
Merge several sorted sequences.
Definition: seqconst.c:190
long seq_repeat(aSubRecord *prec)
Periodically repeat a given set of event codes and time delays. A bit mask selects if each possible r...
Definition: seqconst.c:63
epicsExportRegistrar(asub_evg)
long seq_mask(aSubRecord *prec)
Sequence masker.
Definition: seqconst.c:407
epicsExportAddress(int, seqConstDebug)
#define NINPUTS
Definition: seqconst.c:26
int seqConstDebug
Definition: seqconst.c:28
long seq_shift(aSubRecord *prec)
Sequence shifter.
Definition: seqconst.c:362