#include #include #include #include #include "FACT.h" #include "Dim.h" #include "Event.h" #include "StateMachineDim.h" #include "Connection.h" #include "LocalControl.h" #include "Configuration.h" #include "Console.h" #include "tools.h" #include "HeadersPower.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; class ConnectionInterlock : public Connection { protected: bool fIsValid; private: uint16_t fInterval; bool fIsVerbose; bool fDebugRx; string fSite; string fRdfData; boost::array fArray; string fNextCommand; Time fLastReport; Power::Status fStatus; virtual void Update(const Power::Status &) { } void ProcessAnswer() { if (fDebugRx) { Out() << "------------------------------------------------------" << endl; Out() << fRdfData << endl; Out() << "------------------------------------------------------" << endl; } const size_t p1 = fRdfData.find("\r\n\r\n"); if (p1==string::npos) { Warn("HTTP header not found."); PostClose(false); return; } fRdfData.erase(0, p1+4); fRdfData.insert(0, "\n"); QDomDocument doc; if (!doc.setContent(QString(fRdfData.c_str()), false)) { Warn("Parsing of html failed."); PostClose(false); return; } if (fDebugRx) Out() << "Parsed:\n-------\n" << doc.toString().toStdString() << endl; const QDomNodeList imageElems = doc.elementsByTagName("span"); for (unsigned int i=0; i 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: ConnectionInterlock(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()), fIsValid(false), fIsVerbose(true), fDebugRx(false), fLastReport(Time::none), fKeepAlive(ioservice) { SetLogStream(&imp); } void SetVerbose(bool b) { fIsVerbose = b; } void SetDebugRx(bool b) { fDebugRx = b; Connection::SetVerbose(b); } void SetInterval(uint16_t i) { fInterval = i; } void SetSite(const string &site) { fSite = site; } void Post(const string &post) { fNextCommand = post; } void Request() { string cmd = "GET " + fSite; if (!fNextCommand.empty()) cmd += "?" + fNextCommand; cmd += " HTTP/1.1\r\n"; cmd += "\r\n"; PostMessage(cmd); fNextCommand = ""; fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval)); fKeepAlive.async_wait(boost::bind(&ConnectionInterlock::HandleRequest, this, dummy::error)); } int GetInterval() const { return fInterval; } int GetState() const { using namespace Power::State; // Timeout if (!fLastReport.IsValid() || Time()>fLastReport+boost::posix_time::seconds(fInterval*3)) return Power::State::kDisconnected; // No data received yet if (!fIsValid) return Power::State::kConnected; /* bool fWaterFlowOk; bool fWaterLevelOk; bool fPwrBiasOn; bool fPwr24VOn; bool fPwrPumpOn; bool fPwrDriveOn; bool fDriveMainSwitchOn; bool fDriveFeedbackOn; */ if (!fStatus.fWaterLevelOk || (fStatus.fPwrPumpOn && !fStatus.fWaterFlowOk)) return kCoolingFailure; const int rc = (fStatus.fPwrBiasOn ? kBiasOn : 0) | (fStatus.fPwrPumpOn ? kCameraOn : 0) | (fStatus.fDriveFeedbackOn ? kDriveOn : 0); return rc==0 ? kSystemOff : rc; } }; const uint16_t ConnectionInterlock::kMaxAddr = 0xfff; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" class ConnectionDimWeather : public ConnectionInterlock { private: DimDescribedService fDim; public: ConnectionDimWeather(ba::io_service& ioservice, MessageImp &imp) : ConnectionInterlock(ioservice, imp), fDim("PWR_CONTROL/DATA", "C:1;C:1;C:1;C:1;C:1;C:1;C:1;C:1", "|water_lvl[bool]:Water level ok" "|water_flow[bool]:Water flowing" "|pwr_24V[bool]:24V power enabled" "|pwr_pump[bool]:Pump power enabled" "|pwr_bias[bool]:Bias power enabled" "|pwr_drive[bool]:Drive power enabled (command value)" "|main_drive[bool]:Drive manual main switch on" "|feedback_drive[bool]:Drive power on (feedback value)") { } void Update(const Power::Status &status) { fDim.setQuality(status.GetVal()); fDim.Update(status); } }; // ------------------------------------------------------------------------ template class StateMachinePowerControl : public T, public ba::io_service, public ba::io_service::work { private: S fPower; Time fLastCommand; 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; fPower.SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int SetDebugRx(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetDebugRx", 1)) return T::kSM_FatalError; fPower.SetDebugRx(evt.GetBool()); return T::GetCurrentState(); } int Post(const EventImp &evt) { fPower.Post(evt.GetText()); return T::GetCurrentState(); } int SetCameraPower(const EventImp &evt) { if (!CheckEventSize(evt.GetSize(), "SetCameraPower", 1)) return T::kSM_FatalError; fLastCommand = Time(); fPower.Post(evt.GetBool() ? "cam_on=Camera+ON" : "cam_off=Camera+OFF"); return T::GetCurrentState(); } int ToggleDrive() { fLastCommand = Time(); fPower.Post("dt=Drive+ON%2FOFF"); 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 fPower.GetState(); } public: StateMachinePowerControl(ostream &out=cout) : T(out, "PWR_CONTROL"), ba::io_service::work(static_cast(*this)), fPower(*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 T::AddStateName(Power::State::kDisconnected, "NoConnection", "No connection to web-server could be established recently"); T::AddStateName(Power::State::kConnected, "Connected", "Connection established, but status still not known"); T::AddStateName(Power::State::kSystemOff, "PowerOff", "Camera, Bias and Drive power off"); T::AddStateName(Power::State::kBiasOn, "BiasOn", "Camera and Drive power off, Bias on"); T::AddStateName(Power::State::kDriveOn, "DriveOn", "Camera and Bias power off, Drive on"); T::AddStateName(Power::State::kCameraOn, "CameraOn", "Drive and Bias power off, Camera on"); T::AddStateName(Power::State::kBiasOff, "BiasOff", "Camera and Drive power on, Bias off"); T::AddStateName(Power::State::kDriveOff, "DriveOff", "Camera and Bias power on, Drive off"); T::AddStateName(Power::State::kCameraOff, "CameraOff", "Drive and Bias power on, Camera off"); T::AddStateName(Power::State::kSystemOn, "SystemOn", "Camera, Bias and drive power on"); T::AddStateName(Power::State::kCoolingFailure, "CoolingFailure", "The cooling unit has failed, the interlock has switched off"); // Verbosity commands T::AddEvent("SET_VERBOSE", "B:1") (bind(&StateMachinePowerControl::SetVerbosity, this, placeholders::_1)) ("Set verbosity state" "|verbosity[bool]:disable or enable verbosity for interpreted data (yes/no)"); T::AddEvent("SET_DEBUG_RX", "B:1") (bind(&StateMachinePowerControl::SetDebugRx, this, placeholders::_1)) ("Set debux-rx state" "|debug[bool]:dump received text and parsed text to console (yes/no)"); T::AddEvent("CAMERA_POWER", "B:1") (bind(&StateMachinePowerControl::SetCameraPower, this, placeholders::_1)) ("Switch camera power" "|power[bool]:Switch camera power 'on' or 'off'"); T::AddEvent("TOGGLE_DRIVE") (bind(&StateMachinePowerControl::ToggleDrive, this)) ("Toggle drive power"); T::AddEvent("POST", "C") (bind(&StateMachinePowerControl::Post, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); } int EvalOptions(Configuration &conf) { fPower.SetVerbose(!conf.Get("quiet")); fPower.SetInterval(conf.Get("interval")); fPower.SetDebugTx(conf.Get("debug-tx")); fPower.SetDebugRx(conf.Get("debug-rx")); fPower.SetSite(conf.Get("url")); fPower.SetEndpoint(conf.Get("addr")); fPower.StartConnect(); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" template int RunShell(Configuration &conf) { return Main::execute>(conf); } void SetupConfiguration(Configuration &conf) { po::options_description control("Lid control"); control.add_options() ("no-dim,d", po_switch(), "Disable dim services") ("addr,a", var(""), "Network address of the lid controling Arduino including port") ("url,u", var(""), "File name and path to load") ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.") ("interval,i", var(5), "Interval between two updates on the server in seconds") ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.") ("debug-rx", po_bool(), "Enable debugging for received data.") ; 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 pwrctrl is an interface to the LID control hardware.\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: pwrctrl [-c type] [OPTIONS]\n" " or: pwrctrl [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; // 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); } return 0; }