#include #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" //#include "PixelMap.h" #include "tools.h" #include "LocalControl.h" #include "HeadersFTM.h" #include "HeadersRateScan.h" namespace ba = boost::asio; namespace bs = boost::system; using namespace std; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" #include "DimState.h" // ------------------------------------------------------------------------ class StateMachineRateScan : public StateMachineDim { private: DimVersion fDim; DimDescribedState fDimFTM; DimDescribedService fDimData; DimDescribedService fDimProc; int fCounter; int fCounterMax; int fThreshold; int fThresholdMin; int fThresholdMax; int fThresholdStep; int fThresholdStepDyn; double fRate; double fRateBoard[40]; double fRatePatch[160]; double fOnTime; uint64_t fStartTime; float fResolution; enum reference_t { kCamera, kBoard, kPatch }; reference_t fReference; uint16_t fReferenceIdx; string fCommand; void UpdateProc() { const array v = {{ fThresholdMin, fThresholdMax, fThresholdStep }}; fDimProc.Update(v); } bool CheckEventSize(const EventImp &evt, size_t size) { if (size_t(evt.GetSize())==size) return true; if (evt.GetSize()==0) return false; ostringstream msg; msg << evt.GetName() << " - Received event has " << evt.GetSize() << " bytes, but expected " << size << "."; Fatal(msg); return false; } int HandleTriggerRates(const EventImp &evt) { if (!CheckEventSize(evt, sizeof(FTM::DimTriggerRates))) return GetCurrentState(); if (GetCurrentState()!=RateScan::State::kInProgress) return GetCurrentState(); const FTM::DimTriggerRates &sdata = *static_cast(evt.GetData()); if (++fCounter<0) return GetCurrentState(); if (fCounter==0) { fRate = 0; memset(fRateBoard, 0, 40*sizeof(double)); memset(fRatePatch, 0, 160*sizeof(double)); fOnTime = 0; return GetCurrentState(); } /* if (sdata.fTriggerRate==0) { Message("Rate scan stopped due zero trigger rate."); fThreshold = -1; return; } */ fRate += sdata.fTriggerRate; for (int i=0; i<40; i++) fRateBoard[i] += sdata.fBoardRate[i]; for (int i=0; i<160; i++) fRatePatch[i] += sdata.fPatchRate[i]; double reference = fRate; if (fReference==kBoard) reference = fRateBoard[fReferenceIdx]; if (fReference==kPatch) reference = fRatePatch[fReferenceIdx]; fOnTime += sdata.fOnTime; reference *= sdata.fElapsedTime; if ((reference==0 || sqrt(reference)>fResolution*reference) && fCounter0) out << " (" << sqrt(reference)/reference << ")"; Info(out); return GetCurrentState(); } const double time = sdata.fElapsedTime*fCounter; const uint32_t th = fThreshold; float data[2+3+1+40+160]; memcpy(data, &fStartTime, 8); memcpy(data+2, &th, 4); data[3] = time; // total elapsed time data[4] = fOnTime/time; // relative on time data[5] = fRate/fCounter; for (int i=0; i<40; i++) data[i+6] = fRateBoard[i]/fCounter; for (int i=0; i<160; i++) data[i+46] = fRatePatch[i]/fCounter; ostringstream sout1, sout2, sout3; sout1 << th << " " << data[5]; for (int i=0; i<200; i++) sout2 << " " << data[i+6]; sout3 << " " << data[3] << " " << data[4]; Info(sout1.str()); ofstream fout("ratescan.txt", ios::app); fout << sout1.str() << sout2.str() << sout3.str() << endl; fDimData.setQuality(fCommand=="FTM_CONTROL/SET_THRESHOLD"); fDimData.setData(data, sizeof(data)); fDimData.Update(); fThreshold += fThresholdStep; if (fCounter>=fCounterMax) { Message("Rate scan stopped due to timeout."); return RateScan::State::kConnected; } if (fThreshold>fThresholdMax) { Message("Rate scan finished."); return RateScan::State::kConnected; } // Does this need to be shifted upwards? if (fCounter>1 && fThresholdStepDyn>0) { //const double scale = fCounter/reference/fResolution/fResolution; //const double step = floor(scale*fThresholdStepDyn); fThresholdStep = fCounter*fThresholdStepDyn; } //fCounter = -2; // FIXME: In principle one missed report is enough fCounter = -1; const int32_t cmd[2] = { -1, fThreshold }; DimClient::sendCommandNB(fCommand.c_str(), (void*)cmd, 8); return GetCurrentState(); } int Print() const { Out() << fDim << endl; Out() << fDimFTM << endl; return GetCurrentState(); } int StartRateScan(const EventImp &evt, const string &command) { if (!CheckEventSize(evt, 12)) return kSM_FatalError; fCommand = "FTM_CONTROL/"+command; const int32_t step = evt.Get(8); fThresholdMin = evt.Get(); fThresholdMax = evt.Get(4); fThresholdStep = abs(step); fThresholdStepDyn = step<0 ? -step : 0; UpdateProc(); Dim::SendCommand("FAD_CONTROL/SET_FILE_FORMAT", uint16_t(0)); Dim::SendCommand("FTM_CONTROL/CONFIGURE", string("ratescan")); Message("Configuration for ratescan started."); return RateScan::State::kConfiguring; } int HandleFtmStateChange(/*const EventImp &evt*/) { // All subsystems are not connected if (fDimFTM.state()39) { Error("SetReferenceBoard - Board index out of range [0;39]"); return GetCurrentState(); } fReference = kBoard; fReferenceIdx = evt.GetUInt(); return GetCurrentState(); } int SetReferencePatch(const EventImp &evt) { if (!CheckEventSize(evt, 4)) return kSM_FatalError; if (evt.GetUInt()>159) { Error("SetReferencePatch - Patch index out of range [0;159]"); return GetCurrentState(); } fReference = kPatch; fReferenceIdx = evt.GetUInt(); return GetCurrentState(); } int ChangeStepSize(const EventImp &evt) { if (!CheckEventSize(evt, 4)) return kSM_FatalError; fThresholdStep = evt.Get(); ostringstream msg; msg << "New step size " << fThresholdStep; Info(msg); UpdateProc(); return GetCurrentState(); } int ChangeMaximum(const EventImp &evt) { if (!CheckEventSize(evt, 4)) return kSM_FatalError; fThresholdMax = evt.Get(); return GetCurrentState(); } int Execute() { if (!fDim.online()) return RateScan::State::kDimNetworkNA; return GetCurrentState(); } public: StateMachineRateScan(ostream &out=cout) : StateMachineDim(out, "RATE_SCAN"), fDimFTM("FTM_CONTROL"), fDimData("RATE_SCAN/DATA", "X:1;I:1;F:1;F:1;F:1;F:40;F:160", "|Id[s]:Start time used to identify measurement (UnixTime)" "|Threshold[dac]:Threshold in DAC counts" "|ElapsedTime[s]:Real elapsed time" "|RelOnTime[ratio]:Relative on time" "|TriggerRate[Hz]:Camera trigger rate" "|BoardRate[Hz]:Board trigger rates" "|PatchRate[Hz]:Patch trigger rates"), fDimProc("RATE_SCAN/PROCESS_DATA", "I:1;I:1;I:1", "Rate scan process data" "|min[DAC]:Value at which scan was started" "|max[DAC]:Value at which scan will end" "|step[DAC]:Step size for scan"), fThreshold(-1), fReference(kCamera), fReferenceIdx(0) { // ba::io_service::work is a kind of keep_alive for the loop. // It prevents the io_service to go to stopped state, which // would prevent any consecutive calls to run() // or poll() to do nothing. reset() could also revoke to the // previous state but this might introduce some overhead of // deletion and creation of threads and more. fDim.Subscribe(*this); fDimFTM.Subscribe(*this); fDimFTM.SetCallback(bind(&StateMachineRateScan::HandleFtmStateChange, this)); Subscribe("FTM_CONTROL/TRIGGER_RATES") (bind(&StateMachineRateScan::HandleTriggerRates, this, placeholders::_1)); // State names AddStateName(RateScan::State::kDimNetworkNA, "DimNetworkNotAvailable", "The Dim DNS is not reachable."); AddStateName(RateScan::State::kDisconnected, "Disconnected", "The Dim DNS is reachable, but the required subsystems are not available."); AddStateName(RateScan::State::kConnected, "Connected", "All needed subsystems are connected to their hardware, no action is performed."); AddStateName(RateScan::State::kConfiguring, "Configuring", "Waiting for FTM to get 'Configured'."); AddStateName(RateScan::State::kInProgress, "InProgress", "Rate scan in progress."); AddEvent("START_THRESHOLD_SCAN", "I:3", RateScan::State::kConnected) (bind(&StateMachineRateScan::StartRateScan, this, placeholders::_1, "SET_THRESHOLD")) ("Start rate scan for the threshold in the defined range" "|min[int]:Start value in DAC counts" "|max[int]:Limiting value in DAC counts" "|step[int]:Single step in DAC counts"); AddEvent("START_N_OUT_OF_4_SCAN", "I:3", RateScan::State::kConnected) (bind(&StateMachineRateScan::StartRateScan, this, placeholders::_1, "SET_N_OUT_OF_4")) ("Start rate scan for N-out-of-4 in the defined range" "|min[int]:Start value in DAC counts" "|max[int]:Limiting value in DAC counts" "|step[int]:Single step in DAC counts"); AddEvent("CHANGE_STEP_SIZE", "I:1", RateScan::State::kInProgress) (bind(&StateMachineRateScan::ChangeStepSize, this, placeholders::_1)) ("Change the step size during a ratescan in progress" "|step[int]:Single step in DAC counts"); AddEvent("CHANGE_MAXIMUM", "I:1", RateScan::State::kInProgress) (bind(&StateMachineRateScan::ChangeMaximum, this, placeholders::_1)) ("Change the maximum limit during a ratescan in progress" "|max[int]:Limiting value in DAC counts"); AddEvent("STOP", RateScan::State::kConfiguring, RateScan::State::kInProgress) (bind(&StateMachineRateScan::StopRateScan, this)) ("Stop a ratescan in progress"); AddEvent("SET_REFERENCE_CAMERA", RateScan::State::kDimNetworkNA, RateScan::State::kDisconnected, RateScan::State::kConnected) (bind(&StateMachineRateScan::SetReferenceCamera, this)) ("Use the camera trigger rate as reference for the reolution"); AddEvent("SET_REFERENCE_BOARD", "I:1", RateScan::State::kDimNetworkNA, RateScan::State::kDisconnected, RateScan::State::kConnected) (bind(&StateMachineRateScan::SetReferenceBoard, this, placeholders::_1)) ("Use the given board trigger-rate as reference for the reolution" "|board[idx]:Index of the board (4*crate+board)"); AddEvent("SET_REFERENCE_PATCH", "I:1", RateScan::State::kDimNetworkNA, RateScan::State::kDisconnected, RateScan::State::kConnected) (bind(&StateMachineRateScan::SetReferenceBoard, this, placeholders::_1)) ("Use the given patch trigger-rate as reference for the reolution" "|patch[idx]:Index of the patch (360*crate+36*board+patch)"); AddEvent("PRINT") (bind(&StateMachineRateScan::Print, this)) (""); } int EvalOptions(Configuration &conf) { fCounterMax = conf.Get("max-wait"); fResolution = conf.Get("resolution"); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("Rate scan options"); control.add_options() ("max-wait", var(150), "The maximum number of seconds to wait to get the anticipated resolution for a point.") ("resolution", var(0.05) , "The minimum resolution required for a single data point.") ; conf.AddOptions(control); } /* Extract usage clause(s) [if any] for SYNOPSIS. Translators: "Usage" and "or" here are patterns (regular expressions) which are used to match the usage synopsis in program output. An example from cp (GNU coreutils) which contains both strings: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [OPTION]... SOURCE... DIRECTORY or: cp [OPTION]... -t DIRECTORY SOURCE... */ void PrintUsage() { cout << "The ratescan program is a tool for automation of rate scans.\n" "\n" "Usage: ratescan [-c type] [OPTIONS]\n" " or: ratescan [OPTIONS]\n"; cout << endl; } void PrintHelp() { Main::PrintHelp(); /* Additional help text which is printed after the configuration options goes here */ /* cout << "bla bla bla" << endl << endl; cout << endl; cout << "Environment:" << endl; cout << "environment" << endl; cout << endl; cout << "Examples:" << endl; cout << "test exam" << endl; cout << endl; cout << "Files:" << endl; cout << "files" << endl; cout << endl; */ } int main(int argc, const char* argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); Main::SetupConfiguration(conf); SetupConfiguration(conf); if (!conf.DoParse(argc, argv, PrintHelp)) return 127; //try { // No console access at all if (!conf.Has("console")) { // if (conf.Get("no-dim")) // return RunShell(conf); // else return RunShell(conf); } // Cosole access w/ and w/o Dim /* if (conf.Get("no-dim")) { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } else */ { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } } /*catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; return -1; }*/ return 0; }