#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 "DimServiceInfoList.h" //#include "PixelMap.h" #include "tools.h" #include "LocalControl.h" #include "HeadersFTM.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" // ------------------------------------------------------------------------ class StateMachineRateScan : public StateMachineDim, public DimInfoHandler { /* int Wrap(boost::function f) { f(); return T::GetCurrentState(); } boost::function Wrapper(boost::function func) { return bind(&StateMachineMCP::Wrap, this, func); }*/ private: enum states_t { kStateDimNetworkNA = 1, kStateDisconnected, kStateConnecting, kStateConnected, kStateInProgress, }; // PixelMap fMap; DimServiceInfoList fNetwork; pair fStatusDim; pair fStatusFTM; DimStampedInfo fDim; DimStampedInfo fFTM; DimStampedInfo fRates; DimDescribedService fDimData; DimDescribedService fDimProc; int fSeconds; int fSecondsMax; int fThreshold; int fThresholdMin; int fThresholdMax; int fThresholdStep; 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); } pair GetNewState(DimStampedInfo &info) const { const bool disconnected = info.getSize()==0; // Make sure getTimestamp is called _before_ getTimestampMillisecs const int tsec = info.getTimestamp(); const int tms = info.getTimestampMillisecs(); return make_pair(Time(tsec, tms*1000), disconnected ? -2 : info.getQuality()); } bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; if (has==0) return false; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; Fatal(msg); return false; } void infoHandler() { DimInfo *curr = getInfo(); // get current DimInfo address if (!curr) return; if (curr==&fFTM) { fStatusFTM = GetNewState(fFTM); return; } if (curr==&fDim) { fStatusDim = GetNewState(fDim); fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0; return; } if (curr==&fRates) { if (!CheckEventSize(curr->getSize(), "infoHandler[DimTriggerRates]", sizeof(FTM::DimTriggerRates))) return; if (fThreshold<0/* || fStatusFTM.second!=FTM::kTakingData*/) return; const FTM::DimTriggerRates &sdata = *static_cast(curr->getData()); if (++fSeconds<0) return; if (fSeconds==0) { fRate = 0; memset(fRateBoard, 0, 40*sizeof(double)); memset(fRatePatch, 0, 160*sizeof(double)); fOnTime = 0; return; } 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) && fSeconds0) out << " (" << sqrt(reference)/reference << ")"; Info(out); return; } const double time = sdata.fElapsedTime*fSeconds; 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/fSeconds; for (int i=0; i<40; i++) data[i+6] = fRateBoard[i]/fSeconds; for (int i=0; i<160; i++) data[i+46] = fRatePatch[i]/fSeconds; 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, 204*sizeof(float)); fDimData.Update(); fThreshold += fThresholdStep; if (fSeconds>=fSecondsMax) { Message("Rate scan stopped due to timeout."); fThreshold=-1; return; } if (fThreshold>fThresholdMax) { Message("Rate scan finished."); fThreshold = -1; return; } fSeconds = -2; // FIXME: In principle one missed report is enough const int32_t cmd[2] = { -1, fThreshold }; DimClient::sendCommandNB(fCommand.c_str(), (void*)cmd, 8); } } void PrintState(const pair &state, const char *server) { const State rc = fNetwork.GetState(server, state.second); Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << server << ": "; Out() << rc.name << "[" << rc.index << "]"; Out() << kReset << " - " << kBlue << rc.comment << endl; } int Print() { Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << "DIM_DNS: "; if (fStatusDim.second==0) Out() << "Offline" << endl; else Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl; PrintState(fStatusFTM, "FTM_CONTROL"); return GetCurrentState(); } int StartRateScan(const EventImp &evt, const string &command) { if (!CheckEventSize(evt.GetSize(), "StartRateScan", 12)) return kSM_FatalError; fCommand = "FTM_CONTROL/"+command; fThresholdMin = evt.Get(); fThresholdMax = evt.Get(4); fThresholdStep = evt.Get(8); UpdateProc(); const Time now; fStartTime = trunc(now.UnixTime()); ofstream fout("ratescan.txt", ios::app); fout << "# ----- " << now << " (" << fStartTime << ") -----\n"; fout << "# Command: " << fCommand << '\n'; fout << "# Reference: "; switch (fReference) { case kCamera: fout << "Camera"; break; case kBoard: fout << "Board #" << fReferenceIdx; break; case kPatch: fout << "Patch #" << fReferenceIdx; break; } fout << '\n'; fout << "# -----" << endl; Dim::SendCommand("FAD_CONTROL/SET_FILE_FORMAT", uint16_t(0)); const int32_t data[2] = { -1, fThresholdMin }; //Message("Starting Trigger (FTM)"); //Dim::SendCommand("FTM_CONTROL/SET_PRESCALING", int32_t(20)); Dim::SendCommand(fCommand, data); //Dim::SendCommand("FTM_CONTROL/STOP_TRIGGER"); fThreshold = fThresholdMin; fSeconds = -2; ostringstream msg; msg << "Rate scan " << now << "(" << fStartTime << ") from " << fThresholdMin << " to "; msg << fThresholdMax << " in steps of " << fThresholdStep; msg << " started."; Message(msg); return GetCurrentState(); } int StopRateScan() { fThreshold = -1; Message("Rate scan manually stopped."); //if (fStatusFTM.second==FTM::kTakingData) { //Message("Stopping FTM"); //Dim::SendCommand("FTM_CONTROL/STOP_TRIGGER"); } return GetCurrentState(); } int SetReferenceCamera() { fReference = kCamera; return GetCurrentState(); } int SetReferenceBoard(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetReferenceBoard", 4)) return kSM_FatalError; if (evt.GetUInt()>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.GetSize(), "SetReferencePatch", 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.GetSize(), "ChangeStepSize", 4)) return kSM_FatalError; fThresholdStep = evt.Get(); UpdateProc(); return GetCurrentState(); } int ChangeMaximum(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "ChangeMaximum", 4)) return kSM_FatalError; fThresholdMax = evt.Get(); return GetCurrentState(); } int Execute() { // Dispatch (execute) at most one handler from the queue. In contrary // to run_one(), it doesn't wait until a handler is available // which can be dispatched, so poll_one() might return with 0 // handlers dispatched. The handlers are always dispatched/executed // synchronously, i.e. within the call to poll_one() //poll_one(); if (fStatusDim.second==0) return kStateDimNetworkNA; // All subsystems are not connected if (fStatusFTM.second=FTM::kConnected) return fThreshold<0 ? kStateConnected : kStateInProgress; } public: StateMachineRateScan(ostream &out=cout) : StateMachineDim(out, "RATE_SCAN"), fStatusDim(make_pair(Time(), -2)), fStatusFTM(make_pair(Time(), -2)), fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this), fFTM("FTM_CONTROL/STATE", (void*)NULL, 0, this), fRates("FTM_CONTROL/TRIGGER_RATES", (void*)NULL, 0, this), fDimData("RATE_SCAN/DATA", "X:1;I:1;F:1;F:1;F:1;F:40;F:160", ""), 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. // State names AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable", "The Dim DNS is not reachable."); AddStateName(kStateDisconnected, "Disconnected", "The Dim DNS is reachable, but the required subsystems are not available."); AddStateName(kStateConnected, "Connected", "All needed subsystems are connected to their hardware, no action is performed."); AddStateName(kStateInProgress, "InProgress", "Rate scan in progress."); AddEvent("START_THRESHOLD_SCAN", "I:3", kStateConnected) (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", kStateConnected) (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", kStateInProgress) (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", kStateInProgress) (bind(&StateMachineRateScan::ChangeMaximum, this, placeholders::_1)) ("Change the maximum limit during a ratescan in progress" "|max[int]:Limiting value in DAC counts"); AddEvent("STOP", kStateInProgress) (bind(&StateMachineRateScan::StopRateScan, this)) ("Stop a ratescan in progress"); AddEvent("SET_REFERENCE_CAMERA", kStateDimNetworkNA, kStateDisconnected, kStateConnected) (bind(&StateMachineRateScan::SetReferenceCamera, this)) ("Use the camera trigger rate as reference for the reolution"); AddEvent("SET_REFERENCE_BOARD", "I:1", kStateDimNetworkNA, kStateDisconnected, kStateConnected) (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", kStateDimNetworkNA, kStateDisconnected, kStateConnected) (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) { fSecondsMax = 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 -1; //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; }