#include #include // std::string #include // std::transform #include // std::tolower #include #include "FACT.h" #include "Dim.h" #include "Event.h" #include "StateMachineDim.h" #include "StateMachineAsio.h" #include "Connection.h" #include "LocalControl.h" #include "Configuration.h" #include "Console.h" #include "tools.h" #include "HeadersLid.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; class ConnectionLid : public Connection { protected: struct Lid { int id; float position; float current; string status; Lid(int i) : id(i) { } bool Set(const QDomNamedNodeMap &map) { if (!map.contains("id") || !map.contains("value")) return false; QString item = map.namedItem("id").nodeValue(); QString value = map.namedItem("value").nodeValue(); const char c = '0'+id; if (item==(QString("cur")+c)) { current = value.toFloat(); return true; } if (item==(QString("pos")+c)) { position = value.toFloat(); return true; } if (item==(QString("lid")+c)) { status = value.toStdString(); return true; } return false; } void Print(ostream &out) { out << "Lid" << id << " @ " << position << " / " << current << "A [" << status << "]" << endl; } }; private: uint16_t fInterval; bool fIsVerbose; string fSite; string fRdfData; boost::array fArray; string fNextCommand; Time fLastReport; Lid fLid1; Lid fLid2; virtual void Update(const Lid &, const Lid &) { } void ProcessAnswer() { if (fIsVerbose) { Out() << "------------------------------------------------------" << endl; Out() << fRdfData << endl; Out() << "------------------------------------------------------" << endl; } fRdfData.insert(0, "\n"); QDomDocument doc; if (!doc.setContent(QString(fRdfData.c_str()), false)) { Warn("Parsing of html failed."); return; } if (fIsVerbose) { Out() << "Parsed:\n-------\n" << doc.toString().toStdString() << endl; Out() << "------------------------------------------------------" << endl; } const QDomNodeList imageElems = doc.elementsByTagName("span"); // "input" /* // elementById for (unsigned int i=0; i " << e.text().toStdString() << endl; }*/ for (int i=0; i0) cmd += "\r\n"+args + "\r\n"; cmd += "\r\n"; //cout << "Post: " << cmd << endl; PostMessage(cmd); } 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(); } void ConnectionFailed() { StartConnect(); } public: static const uint16_t kMaxAddr; public: ConnectionLid(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()), fIsVerbose(true), fLastReport(Time::none), fLid1(1), fLid2(2), fKeepAlive(ioservice) { SetLogStream(&imp); } void SetVerbose(bool b) { fIsVerbose = 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; fLid1.status = ""; fLid2.status = ""; //PostRequest("POST", post); } void Request() { PostRequest("POST", fNextCommand); fNextCommand = ""; fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval)); fKeepAlive.async_wait(boost::bind(&ConnectionLid::HandleRequest, this, dummy::error)); } int GetInterval() const { return fInterval; } int GetState() const { using namespace Lid; // Timeout if (fLastReport.IsValid() && fLastReport+boost::posix_time::seconds(fInterval*2) class StateMachineLidControl : public StateMachineAsio { private: S fLid; Time fLastCommand; Time fSunRise; uint16_t fTimeToMove; 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; fLid.SetVerbose(evt.GetBool()); return T::GetCurrentState(); } int Post(const EventImp &evt) { fLid.Post(evt.GetText()); return T::GetCurrentState(); } int Open() { fLastCommand = Time(); fLid.Post("Button5="); return Lid::State::kMoving; } int Close() { fLastCommand = Time(); fLid.Post("Button6="); return Lid::State::kMoving; } /* int MoveMotor(const EventImp &evt, int mid) { if (!CheckEventSize(evt.GetSize(), "MoveMotor", 2)) return T::kSM_FatalError; if (evt.GetUShort()>0xfff) { ostringstream msg; msg << "Position " << evt.GetUShort() << " for motor " << mid+1 << " out of range [0,1023]."; T::Error(msg); return T::GetCurrentState(); } fLid.MoveMotor(mid, evt.GetUShort()); return T::GetCurrentState(); }*/ int Unlock() { return fLid.GetState(); } int Execute() { const int rc = fLid.GetState(); const int state = T::GetCurrentState(); if (state==Lid::State::kMoving && (rc==Lid::State::kConnected || rc==Lid::State::kDisconnected) && fLastCommand+boost::posix_time::seconds(fTimeToMove+fLid.GetInterval()) > Time()) { return Lid::State::kMoving; } const Time now; if (now>fSunRise) { if (state!=Lid::State::kClosed && state!=Lid::State::kLocked && state>Lid::State::kDisconnected) { T::Error("Lidctrl not in 'Closed' at end of nautical twilight!"); Close(); } fSunRise = now.GetNextSunRise(-6); ostringstream msg; msg << "During next sun-rise nautical twilight will end at " << fSunRise; T::Info(msg); return Lid::State::kLocked; } return rc==Lid::State::kConnected ? state : rc; } public: StateMachineLidControl(ostream &out=cout) : StateMachineAsio(out, "LID_CONTROL"), fLid(*this, *this), fSunRise(Time().GetNextSunRise(-6)) { // State names T::AddStateName(Lid::State::kDisconnected, "NoConnection", "No connection to web-server could be established recently"); T::AddStateName(Lid::State::kConnected, "Connected", "Connection established, but status still not known"); T::AddStateName(Lid::State::kUnidentified, "Unidentified", "At least one lid reported a state which could not be identified by lidctrl"); T::AddStateName(Lid::State::kInconsistent, "Inconsistent", "Both lids show different states"); T::AddStateName(Lid::State::kUnknown, "Unknown", "Arduino reports at least one lids in an unknown status"); T::AddStateName(Lid::State::kPowerProblem, "PowerProblem", "Arduino reports both lids to have a power problem (might also be that both are at the end switches)"); T::AddStateName(Lid::State::kOvercurrent, "Overcurrent", "Arduino reports both lids to have a overcurrent (might also be that both are at the end switches)"); T::AddStateName(Lid::State::kClosed, "Closed", "Both lids are closed"); T::AddStateName(Lid::State::kOpen, "Open", "Both lids are open"); T::AddStateName(Lid::State::kMoving, "Moving", "Lids are supposed to move, waiting for next status"); T::AddStateName(Lid::State::kLocked, "Locked", "Locked, no commands accepted except UNLOCK."); // Verbosity commands T::AddEvent("SET_VERBOSE", "B") (bind(&StateMachineLidControl::SetVerbosity, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); T::AddEvent("OPEN", Lid::State::kUnidentified, Lid::State::kInconsistent, Lid::State::kUnknown, Lid::State::kPowerProblem, Lid::State::kClosed) (bind(&StateMachineLidControl::Open, this)) ("Open the lids"); T::AddEvent("CLOSE")(Lid::State::kUnidentified)(Lid::State::kInconsistent)(Lid::State::kUnknown)(Lid::State::kOvercurrent)(Lid::State::kPowerProblem)(Lid::State::kOpen) (bind(&StateMachineLidControl::Close, this)) ("Close the lids"); T::AddEvent("POST", "C")(Lid::State::kUnidentified)(Lid::State::kInconsistent)(Lid::State::kUnknown)(Lid::State::kOvercurrent)(Lid::State::kPowerProblem)(Lid::State::kOpen)(Lid::State::kClosed)(Lid::State::kMoving) (bind(&StateMachineLidControl::Post, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); T::AddEvent("UNLOCK", Lid::State::kLocked) (bind(&StateMachineLidControl::Unlock, this)) ("Unlock if in locked state."); } int EvalOptions(Configuration &conf) { fLid.SetVerbose(!conf.Get("quiet")); fLid.SetInterval(conf.Get("interval")); fLid.SetDebugTx(conf.Get("debug-tx")); fLid.SetSite(conf.Get("url")); fLid.SetEndpoint(conf.Get("addr")); fLid.StartConnect(); fTimeToMove = conf.Get("time-to-move"); 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") ("time-to-move", var(20), "Expected minimum time the lid taks to open/close") ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.") ; 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 lidctrl 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: lidctrl [-c type] [OPTIONS]\n" " or: lidctrl [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; }