#include #include "FACT.h" #include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Timers.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; // ------------------------------------------------------------------------ struct DimWeather { DimWeather() { memset(this, 0, sizeof(DimWeather)); } uint16_t fStatus; float fTemp; float fDew; float fHum; float fPress; float fWind; float fDir; } __attribute__((__packed__)); // ------------------------------------------------------------------------ class ConnectionWeather : public Connection { int fState; bool fIsVerbose; virtual void UpdateWeather(const Time &, const DimWeather &) { } protected: boost::array fArray; Time fLastReport; void HandleRead(const boost::system::error_code& err, size_t bytes_received) { // 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."); // 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; } PostClose(false); const string str(fArray.data(), bytes_received); memset(fArray.data(), 0, fArray.size()); if (fIsVerbose) Out() << str << endl; bool isheader = true; DimWeather data; int hh=0, mm=0, ss=0, y=0, m=0, d=0; stringstream is(str); string line; while (getline(is, line)) { if (line.size()==1 && line[0]==13) { isheader = false; continue; } if (isheader) { /* if (line.substr(0, 16)=="Content-Length: ") { fSize = atoi(line.substr(16).data()); cout << "Size==" << fSize << endl; } if (line.substr(0, 15)=="Last-Modified: ") cout << "Last==" << line.substr(15).data() << endl; */ } else { if (line.substr(0, 2)=="ST") data.fStatus = atoi(line.substr(2).data()); if (line.substr(0, 2)=="TE") data.fTemp = atof(line.substr(2).data()); if (line.substr(0, 2)=="DP") data.fDew = atof(line.substr(2).data()); if (line.substr(0, 3)=="HUM") data.fHum = atof(line.substr(3).data()); if (line.substr(0, 2)=="WS") data.fWind = atof(line.substr(2).data()); if (line.substr(0, 3)=="MWD") data.fDir = atof(line.substr(3).data()); if (line.substr(0, 5)=="PRESS") data.fPress = atof(line.substr(5).data()); if (line.substr(0, 4)=="HOUR") hh = atoi(line.substr(4).data()); if (line.substr(0, 6)=="MINUTS") mm = atoi(line.substr(6).data()); if (line.substr(0, 7)=="SECONDS") ss = atoi(line.substr(7).data()); if (line.substr(0, 4)=="YEAR") y = atoi(line.substr(4).data()); if (line.substr(0, 5)=="MONTH") m = atoi(line.substr(5).data()); if (line.substr(0, 3)=="DAY") d = atoi(line.substr(3).data()); } } try { const Time tm = Time(2000+y, m, d, hh, mm, ss); if (tm==fLastReport) return; ostringstream msg; msg << tm << "[" << data.fStatus << "]: T=" << data.fTemp << "°C H=" << data.fHum << "% P=" << data.fPress << "hPa Td=" << data.fDew << "°C V=" << data.fWind << "km/h dir=" << data.fDir << "deg" << endl; Message(msg); UpdateWeather(tm, data); fLastReport = tm; } catch (const exception &e) { Warn("Corrupted time received."); } } void StartReadReport() { async_read_some(ba::buffer(fArray), boost::bind(&ConnectionWeather::HandleRead, this, dummy::error, dummy::bytes_transferred)); } boost::asio::deadline_timer fKeepAlive; void PostRequest() { const string dir = "/site/weather/weather_data.txt"; const string cmd = "GET "+dir+" HTTP/1.1\r\nHost: www.fact-project.org\r\n\r\n"; PostMessage(cmd); } void Request() { PostRequest(); fKeepAlive.expires_from_now(boost::posix_time::seconds(55)); fKeepAlive.async_wait(boost::bind(&ConnectionWeather::HandleRequest, this, dummy::error)); } void HandleRequest(const bs::error_code &error) { // 125: Operation canceled (bs::error_code(125, bs::system_category)) if (error && error!=ba::error::basic_errors::operation_aborted) { ostringstream str; str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl; Error(str); PostClose(false); 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. PostClose(true); 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 (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now()) return; Request(); } private: // This is called when a connection was established void ConnectionEstablished() { Request(); StartReadReport(); } public: static const uint16_t kMaxAddr; public: ConnectionWeather(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()), fState(-1), fIsVerbose(true), fKeepAlive(ioservice) { SetLogStream(&imp); } void SetVerbose(bool b) { fIsVerbose = b; } int GetState() const { if (!IsConnected()) return 1; if (IsConnected() && fState<0) return 2; return fState+3; } }; const uint16_t ConnectionWeather::kMaxAddr = 0xfff; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" class ConnectionDimDrive : public ConnectionWeather { private: DimDescribedService fDimWeather; virtual void UpdateWeather(const Time &t, const DimWeather &data) { fDimWeather.setData(&data, sizeof(DimWeather)); fDimWeather.Update(t); } public: ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) : ConnectionWeather(ioservice, imp), fDimWeather("MAGIC_WEATHER/DATA", "S:1;F:1;F:1;F:1;F:1;F:1;F:1", "|stat:Status" "|T[deg C]:Temperature" "|T_dew[deg C]:Dew point" "|H[%]:Humidity" "|P[hPa]:Air pressure" "|v[km/h]:Wind speed" "|d[deg]:Wind direction (N-E)") { } }; // ------------------------------------------------------------------------ template class StateMachineDrive : public T, public ba::io_service, public ba::io_service::work { int Wrap(boost::function f) { f(); return T::GetCurrentState(); } boost::function Wrapper(boost::function func) { return bind(&StateMachineDrive::Wrap, this, func); } private: S fWeather; enum states_t { kStateDisconnected = 1, kStateConnected, kStateOk, }; 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; fWeather.SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int Disconnect() { // Close all connections fWeather.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 fWeather.PostClose(false); // Now wait until all connection have been closed and // all pending handlers have been processed poll(); if (evt.GetBool()) fWeather.SetEndpoint(evt.GetString()); // Now we can reopen the connection fWeather.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 fWeather.GetState(); } public: StateMachineDrive(ostream &out=cout) : T(out, "MAGIC_WEATHER"), ba::io_service::work(static_cast(*this)), fWeather(*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", "No connection to cosy"); AddStateName(kStateConnected, "Connected", "Cosy connected, drive stopped"); AddStateName(kStateOk, "Ok", "Drive system not ready for movement"); // Verbosity commands T::AddEvent("SET_VERBOSE", "B") (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); // Conenction commands AddEvent("DISCONNECT", kStateConnected) (bind(&StateMachineDrive::Disconnect, this)) ("disconnect from ethernet"); AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected) (bind(&StateMachineDrive::Reconnect, this, placeholders::_1)) ("(Re)connect ethernet connection to FTM, a new address can be given" "|[host][string]:new ethernet address in the form "); fWeather.StartConnect(); } void SetEndpoint(const string &url) { fWeather.SetEndpoint(url); } int EvalOptions(Configuration &conf) { SetEndpoint(conf.Get("addr")); fWeather.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("Drive control options"); control.add_options() ("no-dim,d", po_switch(), "Disable dim services") ("addr,a", var("www.magic.iac.es:80"), "Network address of Cosy") ("quiet,q", po_bool(true), "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 drivectrl is an interface to cosy.\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: drivectrl [-c type] [OPTIONS]\n" " or: drivectrl [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; }