#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 "tools.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; // ------------------------------------------------------------------------ class ConnectionFSC : public Connection { boost::asio::streambuf fBuffer; bool fIsVerbose; bool fDump; ofstream fDumpStream; protected: virtual void UpdateTemp(float, const vector &) { } virtual void UpdateHum(float, const vector&) { } virtual void UpdateVolt(float, const vector&) { } virtual void UpdateCur(float, const vector&) { } /* virtual void UpdateError() { if (!fIsVerbose) return; Out() << endl << kRed << "Error received:" << endl; Out() << fError; if (fIsHexOutput) Out() << Converter::GetHex(fError, 16) << endl; } */ void Dump(const string &str) { if (!fDumpStream.is_open()) { fDumpStream.open("socket_dump-fsc.txt", ios::app); if (!fDumpStream) { //ostringstream str; //str << "Open file " << name << ": " << strerror(errno) << " (errno=" << errno << ")"; //Error(str); return; } } fDumpStream << str << endl; } private: // // From: http://de.wikipedia.org/wiki/Pt100 // double GetTempPT1000(double R) const { const double R0 = 1000; // 1kOhm const double a = 3.85e-3; return (R/R0 - 1)/a; } void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/) { // Do not schedule a new read if the connection failed. if (bytes_received==0 || err) { if (err==ba::error::eof) Warn("Connection closed by remote host (FTM)."); // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category)) // 125: Operation canceled if (err && err!=ba::error::eof && // Connection closed by remote host err!=ba::error::basic_errors::not_connected && // Connection closed by remote host err!=ba::error::basic_errors::operation_aborted) // Connection closed by us { ostringstream str; str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl; Error(str); } PostClose(err!=ba::error::basic_errors::operation_aborted); return; } if (fIsVerbose) Out() << kBold << "Received (" << bytes_received << " bytes):" << endl; /* "status: 00000538 \n" "time_s: 764.755 \n" "VOLTAGES \n" " \n" "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n" " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n" "values:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 \n" "RESISTANCES \n" " \n" "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n" " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n" "values: \n" "1000.16 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "1197.07 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" " 558.59 677.92 817.26 989.39 1200.35 1503.06 1799.90 2204.18 \n" "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n" "end.\n"; */ /* const unsigned int TIME_OFF = 3; const unsigned int VOLT_OFF = 30; const unsigned int CURR_OFF = 70; const unsigned int HUMI_OFF = 110; const unsigned int TEMP_OFF = 134; */ if (fDump) { ostringstream msg; msg << "--- " << Time().GetAsStr() << " --- received " << bytes_received << " bytes."; Dump(msg.str()); } istream is(&fBuffer); int state = 0; bool values = false; vector volt; vector resist; int status=-1; float time=0; string buffer; while (getline(is, buffer, '\n')) { if (fIsVerbose) Out() << buffer << endl; if (fDump) Dump(buffer); buffer = Tools::Trim(buffer); if (buffer.empty()) continue; if (buffer.substr(0, 4)=="end.") break; if (buffer.substr(0, 8)=="status: ") { status = stoi(buffer.substr(8)); continue; } if (buffer.substr(0, 8)=="time_s: ") { time = stof(buffer.substr(8)); continue; } if (buffer.substr(0, 8)=="VOLTAGES") { state = 1; continue; } if (buffer.substr(0, 11)=="RESISTANCES") { state = 2; continue; } if (state==1 && buffer.substr(0, 7)=="values:") { istringstream in(buffer.substr(7)); while (1) { int v; in >> v; if (!in) break; volt.push_back(v); } continue; } if (state==2 && buffer.substr(0, 7)=="values:") { values = true; continue; } if (state==2 && !values) continue; istringstream in(buffer); while (1) { float f; in >> f; if (!in) break; resist.push_back(f); } } if (volt.size()!=84 || resist.size()!=64) { ostringstream out; out << "Corrupted data received (" << volt.size() << " voltages and "; out << resist.size() << " resistances received; 84 and 64 expected)"; Warn(out); StartRead(); return; } int mapv[] = { 0, 24, 16, 8, 1, 25, 17, 9, 2, 26, 18, 10, // 3, 27, 19, 11, 4, 28, 20, 12, 5, 29, 21, 13, // 32, 36, 33, 34, 37, 38, // -1 }; int mapc[] = { 40, 64, 56, 48, 41, 65, 57, 49, 42, 66, 58, 50, // 43, 67, 59, 51, 44, 68, 60, 52, 45, 69, 61, 53, // 72, 76, 73, 74, 77, 78, // -1 }; int maprh[] = { 80, 81, 82, 83, -1 }; int offrh[] = { 821, 822, 816, 822, }; int mapt[] = { // sensor compartment temperatures 0, 1, 2, 3, 4, 5, 6, 56, 57, 58, 59, 60, 61, 62, 32, 33, 34, 35, 36, 63, 37, 38, 39, 24, 25, 26, 27, 28, 29, 30, 31, // crate temperatures (0-3, back, front) 12, 13, 52, 53, 44, 45, 20, 21, //crate power supply temperatures (0-3) 8, 9, 48, 49, 40, 41, 16, 17, // aux power supplies (FTM-side top/bot, FSC-side top/bot) 10, 50, 18, 42, // backpanel (FTM-side top/bot, FSC-side top/bot) 11, 51, 19, 43, // switch boxes (top front/back, bottom front/back) 15, 14, 47, 46, // -1 }; vector voltages; vector currents; vector humidities; vector temperatures; for (int *pv=mapv; *pv>=0; pv++) voltages.push_back(volt[*pv]*0.001); for (int *pc=mapc; *pc>=0; pc++) currents.push_back(volt[*pc]*0.005); for (int idx=0; idx<4; idx++) { voltages[idx +8] *= -1; voltages[idx+20] *= -1; currents[idx +8] *= -1; currents[idx+20] *= -1; } voltages[26] *= 2; voltages[27] *= -2; voltages[29] *= -1; currents[27] *= -1; currents[29] *= -1; int idx=0; for (int *ph=maprh; *ph>=0; ph++, idx++) humidities.push_back((volt[*ph]-offrh[idx])*0.0313); for (int *pt=mapt; *pt>=0; pt++) temperatures.push_back(resist[*pt]>800&&resist[*pt]<2000 ? GetTempPT1000(resist[*pt]) : 0); // 0 = 3-(3+0)%4 // 3 = 3-(3+1)%4 // 2 = 3-(3+2)%4 // 1 = 3-(3+3)%4 /* index unit offset scale crate for board: 0 mV 0 1 0 FAD 24 mV 0 1 1 FAD 16 mV 0 1 2 FAD 8 mV 0 1 3 FAD 1 mV 0 1 0 FAD 25 mV 0 1 1 FAD 17 mV 0 1 2 FAD 9 mV 0 1 3 FAD 2 mV 0 -1 0 FAD 26 mV 0 -1 1 FAD 18 mV 0 -1 2 FAD 10 mV 0 -1 3 FAD -- 3 mV 0 1 0 FPA 27 mV 0 1 1 FPA 19 mV 0 1 2 FPA 11 mV 0 1 3 FPA 4 mV 0 1 0 FPA 28 mV 0 1 1 FPA 20 mV 0 1 2 FPA 12 mV 0 1 3 FPA 5 mV 0 -1 0 FPA 29 mV 0 -1 1 FPA 21 mV 0 -1 2 FPA 13 mV 0 -1 3 FPA -- 32 mV 0 1 bottom ETH 36 mV 0 1 top ETH 33 mV 0 1 bottom FTM 34 mV 0 -1 bottom FTM 37 mV 0 1 top FFC 38 mV 0 -1 top FLP ----- 40 mA 0 5 0 FAD 64 mA 0 5 1 FAD 56 mA 0 5 2 FAD 48 mA 0 5 3 FAD 41 mA 0 5 0 FAD 65 mA 0 5 1 FAD 57 mA 0 5 2 FAD 49 mA 0 5 3 FAD 42 mA 0 -5 0 FAD 66 mA 0 -5 1 FAD 58 mA 0 -5 2 FAD 50 mA 0 -5 3 FAD -- 43 mA 0 5 0 FPA 67 mA 0 5 1 FPA 59 mA 0 5 2 FPA 51 mA 0 5 3 FPA 44 mA 0 5 0 FPA 68 mA 0 5 1 FPA 60 mA 0 5 2 FPA 52 mA 0 5 3 FPA 45 mA 0 -5 0 FPA 69 mA 0 -5 1 FPA 61 mA 0 -5 2 FPA 53 mA 0 -5 3 FPA --- 72 mA 0 5 bottom ETH 76 mA 0 5 top ETH 73 mA 0 5 bottom FTM 74 mA 0 -5 bottom FTM 77 mA 0 5 top FFC 78 mA 0 -5 top FLP ---- 80 % RH -821 0.0313 FSP000 81 % RH -822 0.0313 FSP221 82 % RH -816 0.0313 Sector0 83 % RH -822 0.0313 Sector2 */ // TEMPERATURES // 31 x Sensor plate // 8 x Crate // 12 x PS // 4 x Backpanel // 4 x Switchbox /* 0 ohms FSP 000 1 ohms FSP 010 2 ohms FSP 023 3 ohms FSP 043 4 ohms FSP 072 5 ohms FSP 080 6 ohms FSP 092 56 ohms FSP 103 57 ohms FSP 111 58 ohms FSP 121 59 ohms FSP 152 60 ohms FSP 163 61 ohms FSP 171 62 ohms FSP 192 32 ohms FSP 200 33 ohms FSP 210 34 ohms FSP 223 35 ohms FSP 233 36 ohms FSP 243 63 ohms FSP 252 37 ohms FSP 280 38 ohms FSP 283 39 ohms FSP 293 24 ohms FSP 311 25 ohms FSP 321 26 ohms FSP 343 27 ohms FSP 352 28 ohms FSP 363 29 ohms FSP 371 30 ohms FSP 381 31 ohms FSP 392 8 ohms Crate0 ? 9 ohms Crate0 ? 48 ohms Crate1 ? 49 ohms Crate1 ? 40 ohms Crate2 ? 41 ohms Crate2 ? 16 ohms Crate3 ? 17 ohms Crate3 ? 10 ohms PS Crate 0 11 ohms PS Crate 0 50 ohms PS Crate 1 51 ohms PS Crate 1 42 ohms PS Crate 2 43 ohms PS Crate 2 18 ohms PS Crate 3 19 ohms PS Crate 3 12 ohms PS Aux0 52 ohms PS Aux0 20 ohms PS Aux1 44 ohms PS Aux1 13 ohms Backpanel ? 21 ohms Backpanel ? 45 ohms Backpanel ? 53 ohms Backpanel ? 14 ohms Switchbox0 ? 15 ohms Switchbox0 ? 46 ohms Switchbox1 ? 47 ohms Switchbox1 ? 7 ohms nc nc 22 ohms nc nc 23 ohms nc nc 54 ohms nc nc 55 ohms nc nc */ for (size_t i=0; i800 && resist[i]<2000) cout << setw(2) << i << " - " << setw(4) << (int)resist[i] << ": " << setprecision(1) << fixed << GetTempPT1000(resist[i]) << endl; else cout << setw(2) << i << " - " << setw(4) << (int)resist[i] << ": " << "----" << endl; UpdateTemp(time, temperatures); UpdateVolt(time, voltages); UpdateCur( time, currents); UpdateHum( time, humidities); StartRead(); } void StartRead() { ba::async_read_until(*this, fBuffer, "end.\n", boost::bind(&ConnectionFSC::HandleReceivedData, this, dummy::error, dummy::bytes_transferred, 0)); // FIXME: Add timeout here } // This is called when a connection was established void ConnectionEstablished() { PostMessage("m", 1); fBuffer.prepare(10000); StartRead(); } /* void HandleReadTimeout(const bs::error_code &error) { if (error==ba::error::basic_errors::operation_aborted) return; if (error) { ostringstream str; str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl; Error(str); PostClose(); return; } if (!is_open()) { // For example: Here we could schedule a new accept if we // would not want to allow two connections at the same time. return; } // Check whether the deadline has passed. We compare the deadline // against the current time since a new asynchronous operation // may have moved the deadline before this actor had a chance // to run. if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now()) return; Error("Timeout reading data from "+URL()); PostClose(); } */ public: ConnectionFSC(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()), fIsVerbose(true), fDump(false) { SetLogStream(&imp); } void SetVerbose(bool b) { fIsVerbose = b; } void SetDumpStream(bool b) { fDump = b; } }; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" class ConnectionDimFSC : public ConnectionFSC { private: DimDescribedService fDimTemp; DimDescribedService fDimHum; DimDescribedService fDimVolt; DimDescribedService fDimCurrent; void Update(DimDescribedService &svc, vector data, float time) const { data.insert(data.begin(), time); svc.Update(data); } void UpdateTemp(float time, const vector &temp) { Update(fDimTemp, temp, time); } void UpdateHum(float time, const vector &hum) { Update(fDimHum, hum, time); } void UpdateVolt(float time, const vector &volt) { Update(fDimVolt, volt, time); } void UpdateCur(float time, const vector &curr) { Update(fDimCurrent, curr, time); } public: ConnectionDimFSC(ba::io_service& ioservice, MessageImp &imp) : ConnectionFSC(ioservice, imp), fDimTemp ("FSC_CONTROL/TEMPERATURE", "F:1;F:31;F:8;F:12;F:4;F:4", ""), fDimHum ("FSC_CONTROL/HUMIDITY", "F:1;F:1;F:1;F:1;F:1", ""), fDimVolt ("FSC_CONTROL/VOLTAGE", "F:1;F:4;F:4;F:4;F:4;F:4;F:4;F:2;F:2;F:1;F:1", ""), fDimCurrent("FSC_CONTROL/CURRENT", "F:1;F:4;F:4;F:4;F:4;F:4;F:4;F:2;F:2;F:1;F:1", "") { } // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z }; // ------------------------------------------------------------------------ template class StateMachineFSC : public T, public ba::io_service, public ba::io_service::work { int Wrap(boost::function f) { f(); return T::GetCurrentState(); } function Wrapper(function func) { return bind(&StateMachineFSC::Wrap, this, func); } private: S fFSC; enum states_t { kStateDisconnected = 1, kStateConnected = 2, }; int Disconnect() { // Close all connections fFSC.PostClose(false); /* // Now wait until all connection have been closed and // all pending handlers have been processed poll(); */ return T::GetCurrentState(); } int Reconnect(const EventImp &evt) { // Close all connections to supress the warning in SetEndpoint fFSC.PostClose(false); // Now wait until all connection have been closed and // all pending handlers have been processed poll(); if (evt.GetBool()) fFSC.SetEndpoint(evt.GetString()); // Now we can reopen the connection fFSC.PostClose(true); return T::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(); return fFSC.IsConnected() ? kStateConnected : kStateDisconnected; } bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; T::Fatal(msg); return false; } int SetVerbosity(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1)) return T::kSM_FatalError; fFSC.SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int SetDumpStream(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetDumpStream", 1)) return T::kSM_FatalError; fFSC.SetDumpStream(evt.GetBool()); return T::GetCurrentState(); } public: StateMachineFSC(ostream &out=cout) : T(out, "FSC_CONTROL"), ba::io_service::work(static_cast(*this)), fFSC(*this, *this) { // 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(kStateDisconnected, "Disconnected", "FSC board not connected via ethernet."); AddStateName(kStateConnected, "Connected", "Ethernet connection to FSC established."); // Verbosity commands T::AddEvent("SET_VERBOSE", "B:1") (bind(&StateMachineFSC::SetVerbosity, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); T::AddEvent("DUMP_STREAM", "B:1") (bind(&StateMachineFSC::SetDumpStream, this, placeholders::_1)) ("" ""); // Conenction commands AddEvent("DISCONNECT", kStateConnected) (bind(&StateMachineFSC::Disconnect, this)) ("disconnect from ethernet"); AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected) (bind(&StateMachineFSC::Reconnect, this, placeholders::_1)) ("(Re)connect ethernet connection to FTM, a new address can be given" "|[host][string]:new ethernet address in the form "); fFSC.StartConnect(); } void SetEndpoint(const string &url) { fFSC.SetEndpoint(url); } int EvalOptions(Configuration &conf) { SetEndpoint(conf.Get("addr")); fFSC.SetVerbose(!conf.Get("quiet")); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute>(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("FTM control options"); control.add_options() ("no-dim", po_bool(), "Disable dim services") ("addr,a", var("localhost:5000"), "Network address of FTM") ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.") ; 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 ftmctrl controls the FSC (FACT Slow Control) board.\n" "\n" "The default is that the program is started without user intercation. " "All actions are supposed to arrive as DimCommands. Using the -c " "option, a local shell can be initialized. With h or help a short " "help message about the usuage can be brought to the screen.\n" "\n" "Usage: fscctrl [-c type] [OPTIONS]\n" " or: fscctrl [OPTIONS]\n"; cout << endl; } void 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; }