source: trunk/FACT++/src/ratecontrol.cc@ 13734

Last change on this file since 13734 was 13718, checked in by tbretz, 13 years ago
Implemented blocking which makes sure that if only a single channel's threshold is set it is not set twice without break. Due to a firmware bug the FTU would stop counting otherwise and we always get the same rate.
File size: 20.5 KB
Line 
1#include <valarray>
2
3#include "Dim.h"
4#include "Event.h"
5#include "Shell.h"
6#include "StateMachineDim.h"
7#include "Connection.h"
8#include "Configuration.h"
9#include "Console.h"
10#include "Converter.h"
11#include "DimServiceInfoList.h"
12//#include "PixelMap.h"
13
14#include "tools.h"
15
16#include "LocalControl.h"
17
18#include "HeadersFTM.h"
19
20namespace ba = boost::asio;
21namespace bs = boost::system;
22namespace dummy = ba::placeholders;
23
24using namespace std;
25
26// ------------------------------------------------------------------------
27
28#include "DimDescriptionService.h"
29
30// ------------------------------------------------------------------------
31
32class StateMachineRateControl : public StateMachineDim, public DimInfoHandler
33{
34private:
35 enum states_t
36 {
37 kStateDimNetworkNA = 1,
38 kStateDisconnected,
39 kStateConnecting,
40 kStateConnected,
41
42 kStateSettingGlobalThreshold,
43 kStateGlobalThresholdSet,
44
45 kStateInProgress,
46 };
47
48 bool fTriggerOn;
49
50 vector<bool> fBlock;
51
52 DimServiceInfoList fNetwork;
53
54 pair<Time, int> fStatusDim;
55 pair<Time, int> fStatusFTM;
56 pair<Time, int> fStatusRS;
57
58 DimStampedInfo fDim;
59 DimStampedInfo fFTM;
60 DimStampedInfo fRates;
61// DimStampedInfo fStatic;
62 DimStampedInfo fRateScan;
63
64// DimDescribedService fDimData;
65// DimDescribedService fDimProc;
66
67 float fTargetRate;
68 float fTriggerRate;
69
70 uint16_t fThresholdMin;
71 uint16_t fThresholdReference;
72
73 bool fVerbose;
74 bool fEnabled;
75
76 uint64_t fCounter;
77
78 pair<Time, int> GetNewState(DimStampedInfo &info) const
79 {
80 const bool disconnected = info.getSize()==0;
81
82 // Make sure getTimestamp is called _before_ getTimestampMillisecs
83 const int tsec = info.getTimestamp();
84 const int tms = info.getTimestampMillisecs();
85
86 return make_pair(Time(tsec, tms*1000),
87 disconnected ? -2 : info.getQuality());
88 }
89
90 bool CheckEventSize(size_t has, const char *name, size_t size)
91 {
92 if (has==size)
93 return true;
94
95 if (has==0)
96 return false;
97
98 ostringstream msg;
99 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
100 Fatal(msg);
101 return false;
102 }
103
104 vector<uint16_t> fThresholds;
105
106 void PrintThresholds(const FTM::DimStaticData &sdata)
107 {
108 //if (!fVerbose)
109 // return;
110
111 if (fThresholds.size()==0)
112 return;
113
114 Out() << "Min. DAC=" << fThresholdMin << endl;
115
116 int t=0;
117 for (t=0; t<160; t++)
118 if (sdata.fThreshold[t]!=fThresholds[t])
119 break;
120
121 if (t==160)
122 return;
123
124 for (int j=0; j<10; j++)
125 {
126 for (int k=0; k<4; k++)
127 {
128 for (int i=0; i<4; i++)
129 if (fThresholds[i+k*4+j*16]!=fThresholdMin)
130 Out() << setw(3) << fThresholds[i+k*4+j*16] << " ";
131 else
132 Out() << " - ";
133 Out() << " ";
134 }
135 Out() << endl;
136 }
137 Out() << endl;
138 }
139
140 void Step(int idx, float step)
141 {
142 uint16_t diff = fThresholds[idx]+int16_t(truncf(step));
143 if (diff<fThresholdMin)
144 diff=fThresholdMin;
145
146 if (diff==fThresholds[idx])
147 return;
148
149 if (fVerbose)
150 {
151 Out() << idx/40 << "|" << (idx/4)%10 << "|" << idx%4;
152 Out() << (step>0 ? " += " : " -= ");
153 Out() << step << " (" << diff << ")" << endl;
154 }
155
156 const uint32_t val[2] = { idx, diff };
157 DimClient::sendCommandNB("FTM_CONTROL/SET_THRESHOLD", (void*)val, 8);
158
159 fBlock[idx] = true;
160 }
161
162 void ProcessPatches(const FTM::DimTriggerRates &sdata)
163 {
164
165 // Caluclate Median and deviation
166 vector<float> medb(sdata.fBoardRate, sdata.fBoardRate+40);
167 vector<float> medp(sdata.fPatchRate, sdata.fPatchRate+160);
168
169 sort(medb.begin(), medb.end());
170 sort(medp.begin(), medp.end());
171
172 vector<float> devb(40);
173 for (int i=0; i<40; i++)
174 devb[i] = fabs(sdata.fBoardRate[i]-medb[i]);
175
176 vector<float> devp(160);
177 for (int i=0; i<160; i++)
178 devp[i] = fabs(sdata.fPatchRate[i]-medp[i]);
179
180 sort(devb.begin(), devb.end());
181 sort(devp.begin(), devp.end());
182
183 double mb = (medb[19]+medb[20])/2;
184 double mp = (medp[79]+medp[80])/2;
185
186 double db = devb[27];
187 double dp = devp[109];
188
189 // If any is zero there is something wrong
190 if (mb==0 || mp==0 || db==0 || dp==0)
191 return;
192
193 if (fVerbose)
194 {
195 Out() << "Patch: Median=" << mp << " Dev=" << dp << endl;
196 Out() << "Board: Median=" << mb << " Dev=" << db << endl;
197 }
198
199 for (int i=0; i<40; i++)
200 {
201 int maxi = -1;
202
203 const float dif = fabs(sdata.fBoardRate[i]-mb)/db;
204 if (dif>5)
205 {
206 if (fVerbose)
207 Out() << "B" << i << ": " << dif << endl;
208
209 float max = sdata.fPatchRate[i*4];
210 maxi = 0;
211
212 for (int j=1; j<4; j++)
213 if (sdata.fPatchRate[i*4+j]>max)
214 {
215 max = sdata.fPatchRate[i*4+j];
216 maxi = j;
217 }
218 }
219
220 vector<bool> block(160);
221
222 for (int j=0; j<4; j++)
223 {
224 if (fBlock[i*4+j])
225 {
226 fBlock[i*4+j] = false;
227 continue;
228 }
229
230 // For the noise pixel correct down to median+3*deviation
231 if (maxi==j)
232 {
233 // This is the step which has to be performed to go from
234 // a NSB rate of sdata.fPatchRate[i*4+j]
235
236
237 const float step = (log10(sdata.fPatchRate[i*4+j])-log10(mp+5*dp))/0.039;
238 // * (dif-5)/dif
239 Step(i*4+j, step);
240 continue;
241 }
242
243 // For pixels below the meadian correct also back to median+3*deviation
244 if (sdata.fPatchRate[i*4+j]<mp)
245 {
246 const float step = (log10(sdata.fPatchRate[i*4+j])-log10(mp+3*dp))/0.039;
247 Step(i*4+j, step);
248 continue;
249 }
250
251 const float step = -1.5*(log10(mp+dp)-log10(mp))/0.039;
252 Step(i*4+j, step);
253 }
254 }
255 }
256
257 void ProcessCamera(const FTM::DimTriggerRates &sdata)
258 {
259 if (fCounter++==0)
260 return;
261
262 // Caluclate Median and deviation
263 vector<float> medb(sdata.fBoardRate, sdata.fBoardRate+40);
264
265 sort(medb.begin(), medb.end());
266
267 vector<float> devb(40);
268 for (int i=0; i<40; i++)
269 devb[i] = fabs(sdata.fBoardRate[i]-medb[i]);
270
271 sort(devb.begin(), devb.end());
272
273 double mb = (medb[19]+medb[20])/2;
274 double db = devb[27];
275
276 // If any is zero there is something wrong
277 if (mb==0 || db==0)
278 {
279 Warn("The median or the deviation of all board rates is zero... cannot calibrate.");
280 return;
281 }
282
283 double avg = 0;
284 int num = 0;
285
286 for (int i=0; i<40; i++)
287 {
288 if ( fabs(sdata.fBoardRate[i]-mb)<2.5*db)
289 {
290 avg += sdata.fBoardRate[i];
291 num++;
292 }
293 }
294
295 fTriggerRate = avg/num * 40;
296
297 if (fVerbose)
298 {
299 Out() << "Board: Median=" << mb << " Dev=" << db << endl;
300 Out() << "Camera: " << fTriggerRate << " (" << sdata.fTriggerRate << ", n=" << num << ")" << endl;
301 Out() << "Target: " << fTargetRate << endl;
302 }
303
304 if (sdata.fTriggerRate<fTriggerRate)
305 fTriggerRate = sdata.fTriggerRate;
306
307 // ----------------------
308
309 /*
310 if (avg>0 && avg<fTargetRate)
311 {
312 // I am assuming here (and at other places) the the answer from the FTM when setting
313 // the new threshold always arrives faster than the next rate update.
314 fThresholdMin = fThresholds[0];
315 Out() << "Setting fThresholdMin to " << fThresholds[0] << endl;
316 }
317 */
318
319 if (fTriggerRate>0 && fTriggerRate<fTargetRate)
320 {
321 fThresholds.assign(160, fThresholdMin);
322 return;
323 }
324
325 // This is a step towards a threshold at which the NSB rate is equal the target rate
326 // +1 to avoid getting a step of 0
327 const float step = (log10(fTriggerRate)-log10(fTargetRate))/0.039 + 1;
328
329 const uint16_t diff = fThresholdMin+int16_t(truncf(step));
330 if (diff<=fThresholdMin)
331 return;
332
333 if (fVerbose)
334 {
335 //Out() << idx/40 << "|" << (idx/4)%10 << "|" << idx%4;
336 Out() << fThresholdMin;
337 Out() << (step>0 ? " += " : " -= ");
338 Out() << step << " (" << diff << ")" << endl;
339 }
340
341 const uint32_t val[2] = { -1, diff };
342 DimClient::sendCommandNB("FTM_CONTROL/SET_THRESHOLD", (void*)val, 8);
343
344 fThresholdMin = diff;
345 }
346
347 void infoHandler()
348 {
349 DimInfo *curr = getInfo(); // get current DimInfo address
350 if (!curr)
351 return;
352
353 if (curr==&fFTM)
354 {
355 fStatusFTM = GetNewState(fFTM);
356 return;
357 }
358
359 if (curr==&fDim)
360 {
361 fStatusDim = GetNewState(fDim);
362 fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0;
363 return;
364 }
365 if (curr==&fRateScan)
366 {
367 fStatusRS = GetNewState(fRateScan);
368 fStatusRS.second = curr->getSize()==4 ? curr->getInt() : 0;
369 return;
370 }
371
372 static vector<uint8_t> counter(160);
373/*
374 if (curr==&fStatic)
375 {
376 if (!CheckEventSize(curr->getSize(), "infoHandler[DimStaticData]", sizeof(FTM::DimStaticData)))
377 return;
378
379 const FTM::DimStaticData &sdata = *static_cast<FTM::DimStaticData*>(curr->getData());
380 fTriggerOn = sdata.HasTrigger();
381
382 PrintThresholds(sdata);
383
384 fThresholds.assign(sdata.fThreshold, sdata.fThreshold+160);
385 return;
386 }
387*/
388 if (curr==&fRates)
389 {
390 if (fThresholds.size()==0)
391 return;
392
393 if (!fTriggerOn && !fEnabled)
394 return;
395
396 if (fStatusRS.second==5)
397 return;
398
399 if (!CheckEventSize(curr->getSize(), "infoHandler[DimTriggerRates]", sizeof(FTM::DimTriggerRates)))
400 return;
401
402 const FTM::DimTriggerRates &sdata = *static_cast<FTM::DimTriggerRates*>(curr->getData());
403
404 if (GetCurrentState()==kStateSettingGlobalThreshold)
405 ProcessCamera(sdata);
406
407 if (GetCurrentState()==kStateInProgress)
408 ProcessPatches(sdata);
409 }
410 }
411
412 int Calibrate()
413 {
414 if (!fTriggerOn)
415 return kStateGlobalThresholdSet;
416
417 const int32_t val[2] = { -1, fThresholdReference };
418 Dim::SendCommand("FTM_CONTROL/SET_THRESHOLD", val);
419
420 fThresholds.assign(160, fThresholdReference);
421
422 fThresholdMin = fThresholdReference;
423 fTriggerRate = -1;
424 fEnabled = true;
425 fCounter = 0;
426
427 return kStateSettingGlobalThreshold;
428 }
429
430 int StartRC()
431 {
432 fEnabled = true;
433 return GetCurrentState();
434 }
435
436 int StopRC()
437 {
438 fEnabled = false;
439 return GetCurrentState();
440 }
441
442 int SetEnabled(const EventImp &evt)
443 {
444 if (!CheckEventSize(evt.GetSize(), "SetEnabled", 1))
445 return kSM_FatalError;
446
447 fEnabled = evt.GetBool();
448
449 return GetCurrentState();
450 }
451
452 int SetMinThreshold(const EventImp &evt)
453 {
454 if (!CheckEventSize(evt.GetSize(), "SetMinThreshold", 4))
455 return kSM_FatalError;
456
457 // FIXME: Check missing
458
459 fThresholdReference = evt.GetUShort();
460
461 return GetCurrentState();
462 }
463
464 int SetTargetRate(const EventImp &evt)
465 {
466 if (!CheckEventSize(evt.GetSize(), "SetTargetRate", 4))
467 return kSM_FatalError;
468
469 fTargetRate = evt.GetFloat();
470
471 return GetCurrentState();
472 }
473
474 void PrintState(const pair<Time,int> &state, const char *server)
475 {
476 const State rc = fNetwork.GetState(server, state.second);
477
478 Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
479 Out() << kBold << server << ": ";
480 Out() << rc.name << "[" << rc.index << "]";
481 Out() << kReset << " - " << kBlue << rc.comment << endl;
482 }
483
484 int Print()
485 {
486 Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
487 Out() << kBold << "DIM_DNS: ";
488 if (fStatusDim.second==0)
489 Out() << "Offline" << endl;
490 else
491 Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl;
492
493 PrintState(fStatusFTM, "FTM_CONTROL");
494
495 return GetCurrentState();
496 }
497
498 int SetVerbosity(const EventImp &evt)
499 {
500 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
501 return kSM_FatalError;
502
503 fVerbose = evt.GetBool();
504
505 return GetCurrentState();
506 }
507
508 int Execute()
509 {
510 // Dispatch (execute) at most one handler from the queue. In contrary
511 // to run_one(), it doesn't wait until a handler is available
512 // which can be dispatched, so poll_one() might return with 0
513 // handlers dispatched. The handlers are always dispatched/executed
514 // synchronously, i.e. within the call to poll_one()
515 //poll_one();
516
517 if (fStatusDim.second==0)
518 return kStateDimNetworkNA;
519
520 // All subsystems are not connected
521 if (fStatusFTM.second<FTM::kConnected)
522 return kStateDisconnected;
523
524 if (GetCurrentState()==kStateSettingGlobalThreshold)
525 {
526 if (fTriggerRate<0 || fTriggerRate>fTargetRate)
527 return kStateSettingGlobalThreshold;
528
529 return kStateGlobalThresholdSet;
530 }
531
532 if (GetCurrentState()==kStateGlobalThresholdSet)
533 {
534 if (!fTriggerOn)
535 return kStateGlobalThresholdSet;
536 //return kStateInProgress;
537 }
538
539 // At least one subsystem is not connected
540 // if (fStatusFTM.second>=FTM::kConnected)
541 return fTriggerOn && fEnabled && fStatusRS.second!=5 ? kStateInProgress : kStateConnected;
542 }
543
544public:
545 StateMachineRateControl(ostream &out=cout) : StateMachineDim(out, "RATE_CONTROL"),
546 fTriggerOn(false), fBlock(160),
547 fStatusDim(make_pair(Time(), -2)),
548 fStatusFTM(make_pair(Time(), -2)),
549 fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this),
550 fFTM("FTM_CONTROL/STATE", (void*)NULL, 0, this),
551 fRates("FTM_CONTROL/TRIGGER_RATES", (void*)NULL, 0, this),
552 fRateScan("RATE_SCAN/STATE", (void*)NULL, 0, this)/*,
553 fStatic("FTM_CONTROL/STATIC_DATA", (void*)NULL, 0, this)/*,
554 fDimData("RATE_SCAN/DATA", "I:1;F:1;F:1;F:1;F:40;F:160", ""),
555 fDimProc("RATE_SCAN/PROCESS_DATA", "I:1;I:1;I:1",
556 "Rate scan process data"
557 "|min[DAC]:Value at which scan was started"
558 "|max[DAC]:Value at which scan will end"
559 "|step[DAC]:Step size for scan")*/
560 {
561 // ba::io_service::work is a kind of keep_alive for the loop.
562 // It prevents the io_service to go to stopped state, which
563 // would prevent any consecutive calls to run()
564 // or poll() to do nothing. reset() could also revoke to the
565 // previous state but this might introduce some overhead of
566 // deletion and creation of threads and more.
567
568 // State names
569 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
570 "The Dim DNS is not reachable.");
571
572 AddStateName(kStateDisconnected, "Disconnected",
573 "The Dim DNS is reachable, but the required subsystems are not available.");
574
575 AddStateName(kStateConnected, "Connected",
576 "All needed subsystems are connected to their hardware, no action is performed.");
577
578 AddStateName(kStateSettingGlobalThreshold, "Calibrating", "");
579 AddStateName(kStateGlobalThresholdSet, "GlobalThresholdSet", "");
580
581 AddStateName(kStateInProgress, "InProgress",
582 "Rate scan in progress.");
583
584 AddEvent("CALIBRATE")
585 (bind(&StateMachineRateControl::Calibrate, this))
586 ("");
587
588 AddEvent("START", "")
589 (bind(&StateMachineRateControl::StartRC, this))
590 ("");
591
592 AddEvent("STOP", "")
593 (bind(&StateMachineRateControl::StopRC, this))
594 ("");
595
596 AddEvent("SET_MIN_THRESHOLD", "I:1")
597 (bind(&StateMachineRateControl::SetMinThreshold, this, placeholders::_1))
598 ("");
599
600 AddEvent("SET_TARGET_RATE", "F:1")
601 (bind(&StateMachineRateControl::SetTargetRate, this, placeholders::_1))
602 ("");
603
604 AddEvent("PRINT")
605 (bind(&StateMachineRateControl::Print, this))
606 ("");
607
608 AddEvent("SET_VERBOSE", "B")
609 (bind(&StateMachineRateControl::SetVerbosity, this, placeholders::_1))
610 ("set verbosity state"
611 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
612
613 }
614
615 int EvalOptions(Configuration &conf)
616 {
617 fVerbose = !conf.Get<bool>("quiet");
618
619 fThresholdReference = 300;
620 fTargetRate = 75;
621
622 return -1;
623 }
624};
625
626// ------------------------------------------------------------------------
627
628#include "Main.h"
629
630template<class T>
631int RunShell(Configuration &conf)
632{
633 return Main::execute<T, StateMachineRateControl>(conf);
634}
635
636void SetupConfiguration(Configuration &conf)
637{
638 po::options_description control("Rate control options");
639 control.add_options()
640 ("quiet,q", po_bool(), "Disable printing more informations during rate control.")
641 //("max-wait", var<uint16_t>(150), "The maximum number of seconds to wait to get the anticipated resolution for a point.")
642 // ("resolution", var<double>(0.05) , "The minimum resolution required for a single data point.")
643 ;
644
645 conf.AddOptions(control);
646}
647
648/*
649 Extract usage clause(s) [if any] for SYNOPSIS.
650 Translators: "Usage" and "or" here are patterns (regular expressions) which
651 are used to match the usage synopsis in program output. An example from cp
652 (GNU coreutils) which contains both strings:
653 Usage: cp [OPTION]... [-T] SOURCE DEST
654 or: cp [OPTION]... SOURCE... DIRECTORY
655 or: cp [OPTION]... -t DIRECTORY SOURCE...
656 */
657void PrintUsage()
658{
659 cout <<
660 "The ratecontrol program is a keep the rate reasonable low.\n"
661 "\n"
662 "Usage: ratecontrol [-c type] [OPTIONS]\n"
663 " or: ratecontrol [OPTIONS]\n";
664 cout << endl;
665}
666
667void PrintHelp()
668{
669 Main::PrintHelp<StateMachineRateControl>();
670
671 /* Additional help text which is printed after the configuration
672 options goes here */
673
674 /*
675 cout << "bla bla bla" << endl << endl;
676 cout << endl;
677 cout << "Environment:" << endl;
678 cout << "environment" << endl;
679 cout << endl;
680 cout << "Examples:" << endl;
681 cout << "test exam" << endl;
682 cout << endl;
683 cout << "Files:" << endl;
684 cout << "files" << endl;
685 cout << endl;
686 */
687}
688
689int main(int argc, const char* argv[])
690{
691 Configuration conf(argv[0]);
692 conf.SetPrintUsage(PrintUsage);
693 Main::SetupConfiguration(conf);
694 SetupConfiguration(conf);
695
696 if (!conf.DoParse(argc, argv, PrintHelp))
697 return -1;
698
699 //try
700 {
701 // No console access at all
702 if (!conf.Has("console"))
703 {
704// if (conf.Get<bool>("no-dim"))
705// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
706// else
707 return RunShell<LocalStream>(conf);
708 }
709 // Cosole access w/ and w/o Dim
710/* if (conf.Get<bool>("no-dim"))
711 {
712 if (conf.Get<int>("console")==0)
713 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
714 else
715 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
716 }
717 else
718*/ {
719 if (conf.Get<int>("console")==0)
720 return RunShell<LocalShell>(conf);
721 else
722 return RunShell<LocalConsole>(conf);
723 }
724 }
725 /*catch (std::exception& e)
726 {
727 cerr << "Exception: " << e.what() << endl;
728 return -1;
729 }*/
730
731 return 0;
732}
Note: See TracBrowser for help on using the repository browser.