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

Last change on this file since 13746 was 13746, checked in by tbretz, 12 years ago
Added some more informal output, added teh fStatc again - I don't understand why I removed that.
File size: 20.8 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 if (curr==&fStatic)
374 {
375 if (!CheckEventSize(curr->getSize(), "infoHandler[DimStaticData]", sizeof(FTM::DimStaticData)))
376 return;
377
378 const FTM::DimStaticData &sdata = *static_cast<FTM::DimStaticData*>(curr->getData());
379 fTriggerOn = sdata.HasTrigger();
380
381 PrintThresholds(sdata);
382
383 fThresholds.assign(sdata.fThreshold, sdata.fThreshold+160);
384 return;
385 }
386
387 if (curr==&fRates)
388 {
389 if (fThresholds.size()==0)
390 return;
391
392 if (!fTriggerOn && !fEnabled)
393 return;
394
395 if (fStatusRS.second==5)
396 return;
397
398 if (!CheckEventSize(curr->getSize(), "infoHandler[DimTriggerRates]", sizeof(FTM::DimTriggerRates)))
399 return;
400
401 const FTM::DimTriggerRates &sdata = *static_cast<FTM::DimTriggerRates*>(curr->getData());
402
403 if (GetCurrentState()==kStateSettingGlobalThreshold)
404 ProcessCamera(sdata);
405
406 if (GetCurrentState()==kStateInProgress)
407 ProcessPatches(sdata);
408 }
409 }
410
411 int Calibrate()
412 {
413 if (!fTriggerOn)
414 {
415 Info("Trigger not switched on... CALIBRATE command ignored.");
416 return kStateGlobalThresholdSet;
417 }
418
419 const int32_t val[2] = { -1, fThresholdReference };
420 Dim::SendCommand("FTM_CONTROL/SET_THRESHOLD", val);
421
422 fThresholds.assign(160, fThresholdReference);
423
424 fThresholdMin = fThresholdReference;
425 fTriggerRate = -1;
426 fEnabled = true;
427 fCounter = 0;
428
429 ostringstream out;
430 out << "Rate calibration started at a threshold of " << fThresholdReference << " with a target rate of " << fTargetRate << " Hz";
431 Info(out);
432
433 return kStateSettingGlobalThreshold;
434 }
435
436 int StartRC()
437 {
438 fEnabled = true;
439 return GetCurrentState();
440 }
441
442 int StopRC()
443 {
444 fEnabled = false;
445 return GetCurrentState();
446 }
447
448 int SetEnabled(const EventImp &evt)
449 {
450 if (!CheckEventSize(evt.GetSize(), "SetEnabled", 1))
451 return kSM_FatalError;
452
453 fEnabled = evt.GetBool();
454
455 return GetCurrentState();
456 }
457
458 int SetMinThreshold(const EventImp &evt)
459 {
460 if (!CheckEventSize(evt.GetSize(), "SetMinThreshold", 4))
461 return kSM_FatalError;
462
463 // FIXME: Check missing
464
465 fThresholdReference = evt.GetUShort();
466
467 return GetCurrentState();
468 }
469
470 int SetTargetRate(const EventImp &evt)
471 {
472 if (!CheckEventSize(evt.GetSize(), "SetTargetRate", 4))
473 return kSM_FatalError;
474
475 fTargetRate = evt.GetFloat();
476
477 return GetCurrentState();
478 }
479
480 void PrintState(const pair<Time,int> &state, const char *server)
481 {
482 const State rc = fNetwork.GetState(server, state.second);
483
484 Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
485 Out() << kBold << server << ": ";
486 Out() << rc.name << "[" << rc.index << "]";
487 Out() << kReset << " - " << kBlue << rc.comment << endl;
488 }
489
490 int Print()
491 {
492 Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - ";
493 Out() << kBold << "DIM_DNS: ";
494 if (fStatusDim.second==0)
495 Out() << "Offline" << endl;
496 else
497 Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl;
498
499 PrintState(fStatusRS, "RATE_SCAN");
500 PrintState(fStatusFTM, "FTM_CONTROL");
501
502 return GetCurrentState();
503 }
504
505 int SetVerbosity(const EventImp &evt)
506 {
507 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
508 return kSM_FatalError;
509
510 fVerbose = evt.GetBool();
511
512 return GetCurrentState();
513 }
514
515 int Execute()
516 {
517 // Dispatch (execute) at most one handler from the queue. In contrary
518 // to run_one(), it doesn't wait until a handler is available
519 // which can be dispatched, so poll_one() might return with 0
520 // handlers dispatched. The handlers are always dispatched/executed
521 // synchronously, i.e. within the call to poll_one()
522 //poll_one();
523
524 if (fStatusDim.second==0)
525 return kStateDimNetworkNA;
526
527 // All subsystems are not connected
528 if (fStatusFTM.second<FTM::kConnected)
529 return kStateDisconnected;
530
531 if (GetCurrentState()==kStateSettingGlobalThreshold)
532 {
533 if (fTriggerRate<0 || fTriggerRate>fTargetRate)
534 return kStateSettingGlobalThreshold;
535
536 return kStateGlobalThresholdSet;
537 }
538
539 if (GetCurrentState()==kStateGlobalThresholdSet)
540 {
541 if (!fTriggerOn)
542 return kStateGlobalThresholdSet;
543 //return kStateInProgress;
544 }
545
546 // At least one subsystem is not connected
547 // if (fStatusFTM.second>=FTM::kConnected)
548 return fTriggerOn && fEnabled && fStatusRS.second!=5 ? kStateInProgress : kStateConnected;
549 }
550
551public:
552 StateMachineRateControl(ostream &out=cout) : StateMachineDim(out, "RATE_CONTROL"),
553 fTriggerOn(false), fBlock(160),
554 fStatusDim(make_pair(Time(), -2)),
555 fStatusFTM(make_pair(Time(), -2)),
556 fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this),
557 fFTM("FTM_CONTROL/STATE", (void*)NULL, 0, this),
558 fRates("FTM_CONTROL/TRIGGER_RATES", (void*)NULL, 0, this),
559 fStatic("FTM_CONTROL/STATIC_DATA", (void*)NULL, 0, this),
560 fRateScan("RATE_SCAN/STATE", (void*)NULL, 0, this)/*,
561 fDimData("RATE_SCAN/DATA", "I:1;F:1;F:1;F:1;F:40;F:160", ""),
562 fDimProc("RATE_SCAN/PROCESS_DATA", "I:1;I:1;I:1",
563 "Rate scan process data"
564 "|min[DAC]:Value at which scan was started"
565 "|max[DAC]:Value at which scan will end"
566 "|step[DAC]:Step size for scan")*/
567 {
568 // ba::io_service::work is a kind of keep_alive for the loop.
569 // It prevents the io_service to go to stopped state, which
570 // would prevent any consecutive calls to run()
571 // or poll() to do nothing. reset() could also revoke to the
572 // previous state but this might introduce some overhead of
573 // deletion and creation of threads and more.
574
575 // State names
576 AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable",
577 "The Dim DNS is not reachable.");
578
579 AddStateName(kStateDisconnected, "Disconnected",
580 "The Dim DNS is reachable, but the required subsystems are not available.");
581
582 AddStateName(kStateConnected, "Connected",
583 "All needed subsystems are connected to their hardware, no action is performed.");
584
585 AddStateName(kStateSettingGlobalThreshold, "Calibrating", "");
586 AddStateName(kStateGlobalThresholdSet, "GlobalThresholdSet", "");
587
588 AddStateName(kStateInProgress, "InProgress",
589 "Rate scan in progress.");
590
591 AddEvent("CALIBRATE")
592 (bind(&StateMachineRateControl::Calibrate, this))
593 ("");
594
595 AddEvent("START", "")
596 (bind(&StateMachineRateControl::StartRC, this))
597 ("");
598
599 AddEvent("STOP", "")
600 (bind(&StateMachineRateControl::StopRC, this))
601 ("");
602
603 AddEvent("SET_MIN_THRESHOLD", "I:1")
604 (bind(&StateMachineRateControl::SetMinThreshold, this, placeholders::_1))
605 ("");
606
607 AddEvent("SET_TARGET_RATE", "F:1")
608 (bind(&StateMachineRateControl::SetTargetRate, this, placeholders::_1))
609 ("");
610
611 AddEvent("PRINT")
612 (bind(&StateMachineRateControl::Print, this))
613 ("");
614
615 AddEvent("SET_VERBOSE", "B")
616 (bind(&StateMachineRateControl::SetVerbosity, this, placeholders::_1))
617 ("set verbosity state"
618 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
619
620 }
621
622 int EvalOptions(Configuration &conf)
623 {
624 fVerbose = !conf.Get<bool>("quiet");
625
626 fThresholdReference = 300;
627 fTargetRate = 75;
628
629 return -1;
630 }
631};
632
633// ------------------------------------------------------------------------
634
635#include "Main.h"
636
637template<class T>
638int RunShell(Configuration &conf)
639{
640 return Main::execute<T, StateMachineRateControl>(conf);
641}
642
643void SetupConfiguration(Configuration &conf)
644{
645 po::options_description control("Rate control options");
646 control.add_options()
647 ("quiet,q", po_bool(), "Disable printing more informations during rate control.")
648 //("max-wait", var<uint16_t>(150), "The maximum number of seconds to wait to get the anticipated resolution for a point.")
649 // ("resolution", var<double>(0.05) , "The minimum resolution required for a single data point.")
650 ;
651
652 conf.AddOptions(control);
653}
654
655/*
656 Extract usage clause(s) [if any] for SYNOPSIS.
657 Translators: "Usage" and "or" here are patterns (regular expressions) which
658 are used to match the usage synopsis in program output. An example from cp
659 (GNU coreutils) which contains both strings:
660 Usage: cp [OPTION]... [-T] SOURCE DEST
661 or: cp [OPTION]... SOURCE... DIRECTORY
662 or: cp [OPTION]... -t DIRECTORY SOURCE...
663 */
664void PrintUsage()
665{
666 cout <<
667 "The ratecontrol program is a keep the rate reasonable low.\n"
668 "\n"
669 "Usage: ratecontrol [-c type] [OPTIONS]\n"
670 " or: ratecontrol [OPTIONS]\n";
671 cout << endl;
672}
673
674void PrintHelp()
675{
676 Main::PrintHelp<StateMachineRateControl>();
677
678 /* Additional help text which is printed after the configuration
679 options goes here */
680
681 /*
682 cout << "bla bla bla" << endl << endl;
683 cout << endl;
684 cout << "Environment:" << endl;
685 cout << "environment" << endl;
686 cout << endl;
687 cout << "Examples:" << endl;
688 cout << "test exam" << endl;
689 cout << endl;
690 cout << "Files:" << endl;
691 cout << "files" << endl;
692 cout << endl;
693 */
694}
695
696int main(int argc, const char* argv[])
697{
698 Configuration conf(argv[0]);
699 conf.SetPrintUsage(PrintUsage);
700 Main::SetupConfiguration(conf);
701 SetupConfiguration(conf);
702
703 if (!conf.DoParse(argc, argv, PrintHelp))
704 return -1;
705
706 //try
707 {
708 // No console access at all
709 if (!conf.Has("console"))
710 {
711// if (conf.Get<bool>("no-dim"))
712// return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
713// else
714 return RunShell<LocalStream>(conf);
715 }
716 // Cosole access w/ and w/o Dim
717/* if (conf.Get<bool>("no-dim"))
718 {
719 if (conf.Get<int>("console")==0)
720 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
721 else
722 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
723 }
724 else
725*/ {
726 if (conf.Get<int>("console")==0)
727 return RunShell<LocalShell>(conf);
728 else
729 return RunShell<LocalConsole>(conf);
730 }
731 }
732 /*catch (std::exception& e)
733 {
734 cerr << "Exception: " << e.what() << endl;
735 return -1;
736 }*/
737
738 return 0;
739}
Note: See TracBrowser for help on using the repository browser.