1 #include <boost/array.hpp> 7 #include <QtXml/QDomDocument> 23 namespace ba = boost::asio;
24 namespace bs = boost::system;
25 namespace dummy = ba::placeholders;
43 bool Set(
const QDomNamedNodeMap &map)
45 if (!map.contains(
"id") || !map.contains(
"value"))
48 QString
item = map.namedItem(
"id").nodeValue();
49 QString value = map.namedItem(
"value").nodeValue();
51 const char c =
'0'+
id;
53 if (item==(QString(
"cur")+c))
55 current = value.toFloat();
59 if (item==(QString(
"pos")+c))
61 position = value.toFloat();
65 if (item==(QString(
"lid")+c))
67 status = value.toStdString();
76 out <<
"Lid" <<
id <<
" @ " << position <<
" / " << current <<
"A [" << status <<
"]" << endl;
107 Out() <<
"------------------------------------------------------" << endl;
108 Out() << fRdfData << endl;
109 Out() <<
"------------------------------------------------------" << endl;
112 fRdfData.insert(0,
"<?xml version=\"1.0\"?>\n");
115 if (!doc.setContent(QString(fRdfData.c_str()),
false))
117 Warn(
"Parsing of html failed.");
123 Out() <<
"Parsed:\n-------\n" << doc.toString().toStdString() << endl;
124 Out() <<
"------------------------------------------------------" << endl;
127 const QDomNodeList imageElems = doc.elementsByTagName(
"span");
146 for (
unsigned int i=0;
i<imageElems.length();
i++)
148 const QDomElement e = imageElems.item(
i).toElement();
150 const QDomNamedNodeMap att = e.attributes();
160 Out() <<
"------------------------------------------------------" << endl;
163 Update(fLid1, fLid2);
167 if ((fLid1.
status!=
"Open" && fLid1.
status!=
"Closed" && fLid1.
status!=
"Power Problem" && fLid1.
status!=
"Unknown" && fLid1.
status!=
"Overcurrent") ||
169 Warn(
"Lid reported status unknown by lidctrl ("+fLid1.
status+
"/"+fLid2.
status+
")");
171 fLastReport =
Time();
174 void HandleRead(
const boost::system::error_code& err,
size_t bytes_received)
177 if (bytes_received==0 || err)
179 if (err==ba::error::eof)
189 if (err && err!=ba::error::eof &&
190 err!=ba::error::basic_errors::not_connected &&
191 err!=ba::error::basic_errors::operation_aborted)
194 str <<
"Reading from " << URL() <<
": " << err.message() <<
" (" << err <<
")";
197 PostClose(err!=ba::error::basic_errors::operation_aborted);
203 fRdfData += string(fArray.data(), bytes_received);
208 const size_t p1 = fRdfData.find(
"\r\n\r\n");
209 if (p1!=string::npos)
212 const size_t p2 = fRdfData.find(
"\r\n\r\n", p1+4);
213 if (p2!=string::npos)
229 dummy::error, dummy::bytes_transferred));
236 cmd +=
" "+fSite+
" HTTP/1.1\r\n" 241 msg << args.length();
243 cmd +=
"Content-Length: ";
248 cmd +=
"\r\n"+args +
"\r\n";
259 if (error && error!=ba::error::basic_errors::operation_aborted)
262 str <<
"Write timeout of " << URL() <<
": " << error.message() <<
" (" << error <<
")";
281 if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
306 fIsVerbose(true), fLastReport(
Time::none),
307 fLid1(1), fLid2(2), fKeepAlive(ioservice)
339 PostRequest(
"POST", fNextCommand);
342 fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval));
344 this, dummy::error));
357 if (fLastReport.
IsValid() && fLastReport+boost::posix_time::seconds(fInterval*2)<
Time())
366 if (fLid1.
status==
"Closed" && fLid2.
status==
"Power Problem")
368 if (fLid2.
status==
"Closed" && fLid1.
status==
"Power Problem")
370 if (fLid1.
status==
"Open" && fLid2.
status==
"Power Problem")
372 if (fLid2.
status==
"Open" && fLid1.
status==
"Power Problem")
380 if (fLid1.
status==
"Unknown")
384 if (fLid1.
status==
"Power Problem")
388 if (fLid1.
status==
"Overcurrent")
392 if (fLid1.
status==
"Closed")
417 fDim(
"LID_CONTROL/DATA",
"S:2;F:2;F:2",
418 "|status[bool]:Lid1/2 open or closed" 419 "|I[A]:Lid1/2 current" 420 "|P[dac]:Lid1/2 hall sensor position in averaged dac counts")
432 DimData() { status[0] = status[1] = -1; }
440 if (l1.
status==
"Power Problem")
449 if (l2.
status==
"Power Problem")
469 template <
class T,
class S>
485 msg << name <<
" - Received event has " << has <<
" bytes, but expected " << size <<
".";
492 if (!CheckEventSize(evt.
GetSize(),
"SetVerbosity", 1))
493 return T::kSM_FatalError;
495 fLid.SetVerbose(evt.
GetBool());
497 return T::GetCurrentState();
503 return T::GetCurrentState();
508 fLastCommand =
Time();
509 fLid.Post(
"Button5=");
514 fLastCommand =
Time();
515 fLid.Post(
"Button6=");
540 return fLid.GetState();
545 const int rc = fLid.GetState();
546 const int state = T::GetCurrentState();
550 fLastCommand+boost::posix_time::seconds(fTimeToMove+fLid.GetInterval()) >
Time())
560 T::Error(
"Lidctrl not in 'Closed' at end of nautical twilight!");
567 msg <<
"During next sun-rise nautical twilight will end at " << fSunRise;
580 fSunRise(
Time().GetNextSunRise(-6))
584 "No connection to web-server could be established recently");
587 "Connection established, but status still not known");
590 "At least one lid reported a state which could not be identified by lidctrl");
593 "Both lids show different states");
596 "Arduino reports at least one lids in an unknown status");
599 "Arduino reports both lids to have a power problem (might also be that both are at the end switches)");
602 "Arduino reports both lids to have a overcurrent (might also be that both are at the end switches)");
605 "Both lids are closed");
608 "Both lids are open");
611 "Lids are supposed to move, waiting for next status");
614 "Locked, no commands accepted except UNLOCK.");
618 T::AddEvent(
"SET_VERBOSE",
"B")
620 (
"set verbosity state" 621 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
633 (
"set verbosity state" 634 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
638 (
"Unlock if in locked state.");
643 fLid.SetVerbose(!conf.
Get<
bool>(
"quiet"));
644 fLid.SetInterval(conf.
Get<uint16_t>(
"interval"));
645 fLid.SetDebugTx(conf.
Get<
bool>(
"debug-tx"));
646 fLid.SetSite(conf.
Get<
string>(
"url"));
647 fLid.SetEndpoint(conf.
Get<
string>(
"addr"));
650 fTimeToMove = conf.
Get<uint16_t>(
"time-to-move");
661 template<
class T,
class S,
class R>
664 return Main::execute<T, StateMachineLidControl<S, R>>(conf);
669 po::options_description control(
"Lid control");
670 control.add_options()
671 (
"no-dim,d",
po_switch(),
"Disable dim services")
672 (
"addr,a", var<string>(
""),
"Network address of the lid controling Arduino including port")
673 (
"url,u", var<string>(
""),
"File name and path to load")
674 (
"quiet,q",
po_bool(
true),
"Disable printing contents of all received messages (except dynamic data) in clear text.")
675 (
"interval,i", var<uint16_t>(5),
"Interval between two updates on the server in seconds")
676 (
"time-to-move", var<uint16_t>(20),
"Expected minimum time the lid taks to open/close")
677 (
"debug-tx",
po_bool(),
"Enable debugging of ethernet transmission.")
695 "The lidctrl is an interface to the LID control hardware.\n" 697 "The default is that the program is started without user intercation. " 698 "All actions are supposed to arrive as DimCommands. Using the -c " 699 "option, a local shell can be initialized. With h or help a short " 700 "help message about the usuage can be brought to the screen.\n" 702 "Usage: lidctrl [-c type] [OPTIONS]\n" 703 " or: lidctrl [OPTIONS]\n";
729 int main(
int argc,
const char* argv[])
740 if (!conf.
Has(
"console"))
742 if (conf.
Get<
bool>(
"no-dim"))
743 return RunShell<LocalStream, StateMachine, ConnectionLid>(conf);
745 return RunShell<LocalStream, StateMachineDim, ConnectionDimWeather>(conf);
748 if (conf.
Get<
bool>(
"no-dim"))
750 if (conf.
Get<
int>(
"console")==0)
751 return RunShell<LocalShell, StateMachine, ConnectionLid>(conf);
753 return RunShell<LocalConsole, StateMachine, ConnectionLid>(conf);
757 if (conf.
Get<
int>(
"console")==0)
758 return RunShell<LocalShell, StateMachineDim, ConnectionDimWeather>(conf);
760 return RunShell<LocalConsole, StateMachineDim, ConnectionDimWeather>(conf);
int Post(const EventImp &evt)
void SetSite(const string &site)
boost::array< char, 4096 > fArray
A general base-class describing events issues in a state machine.
const char * GetText() const
void ConnectionEstablished()
boost::asio::deadline_timer fKeepAlive
void Post(const string &post)
void SetupConfiguration(Configuration &conf)
void setQuality(int quality)
The base implementation of a distributed messaging system.
bool Set(const QDomNamedNodeMap &map)
Adds some functionality to boost::posix_time::ptime for our needs.
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
Time GetNextSunRise(double horizon) const
po::typed_value< bool > * po_switch()
virtual void Update(const Lid &, const Lid &)
bool Has(const std::string &var)
ConnectionLid(ba::io_service &ioservice, MessageImp &imp)
void AddOptions(const po::options_description &opt, bool visible=true)
void SetInterval(uint16_t i)
void SetVerbose(bool b=true)
int EvalOptions(Configuration &conf)
void Update(const Lid &l1, const Lid &l2)
Commandline parsing, resource file parsing and database access.
bool CheckEventSize(size_t has, const char *name, size_t size)
static const uint16_t kMaxAddr
StateMachineLidControl(ostream &out=cout)
int SetVerbosity(const EventImp &evt)
void PostRequest(string cmd, const string &args="")
boost::asio::streambuf fBuffer
void HandleRead(const boost::system::error_code &err, size_t bytes_received)
po::typed_value< bool > * po_bool(bool def=false)
int main(int argc, const char *argv[])
Maintains an ansynchronous TCP/IP client connection.
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
void SetupConfiguration(Configuration &conf)
ConnectionDimWeather(ba::io_service &ioservice, MessageImp &imp)
void HandleRequest(const bs::error_code &error)
int RunShell(Configuration &conf)
virtual size_t GetSize() const