FACT++  1.0
pwrctrl.cc
Go to the documentation of this file.
1 #include <boost/array.hpp>
2 
3 #include <string>
4 
5 #include <QtXml/QDomDocument>
6 
7 #include "FACT.h"
8 #include "Dim.h"
9 #include "Event.h"
10 #include "StateMachineDim.h"
11 #include "StateMachineAsio.h"
12 #include "Connection.h"
13 #include "LocalControl.h"
14 #include "Configuration.h"
15 #include "Console.h"
16 
17 #include "tools.h"
18 
19 #include "HeadersPower.h"
20 
21 namespace ba = boost::asio;
22 namespace bs = boost::system;
23 namespace dummy = ba::placeholders;
24 
25 using namespace std;
26 
27 class ConnectionInterlock : public Connection
28 {
29 protected:
30  bool fIsValid;
31 
32 private:
33  uint16_t fInterval;
34 
35  bool fIsVerbose;
36  bool fDebugRx;
37 
38  string fSite;
39  string fRdfData;
40 
41  boost::array<char, 4096> fArray;
42 
43  string fNextCommand;
44 
46 
48 
49  virtual void Update(const Power::Status &)
50  {
51  }
52 
53 
55  {
56  if (fDebugRx)
57  {
58  Out() << "------------------------------------------------------" << endl;
59  Out() << fRdfData << endl;
60  Out() << "------------------------------------------------------" << endl;
61  }
62 
63  const size_t p1 = fRdfData.find("\r\n\r\n");
64  if (p1==string::npos)
65  {
66  Warn("HTTP header not found.");
67  PostClose(false);
68  return;
69  }
70 
71  fRdfData.erase(0, p1+4);
72  fRdfData.insert(0, "<?xml version=\"1.0\"?>\n");
73 
74  QDomDocument doc;
75  if (!doc.setContent(QString(fRdfData.c_str()), false))
76  {
77  Warn("Parsing of html failed.");
78  PostClose(false);
79  return;
80  }
81 
82  if (fDebugRx)
83  Out() << "Parsed:\n-------\n" << doc.toString().toStdString() << endl;
84 
85  const QDomNodeList imageElems = doc.elementsByTagName("span");
86 
87  for (unsigned int i=0; i<imageElems.length(); i++)
88  {
89  const QDomElement e = imageElems.item(i).toElement();
90 
91  const QDomNamedNodeMap att = e.attributes();
92 
93  if (fStatus.Set(att))
94  fIsValid = true;
95  }
96 
97  if (fIsVerbose)
98  fStatus.Print(Out());
99 
100  Update(fStatus);
101 
102  fRdfData = "";
103 
104  fLastReport = Time();
105  PostClose(false);
106  }
107 
108  void HandleRead(const boost::system::error_code& err, size_t bytes_received)
109  {
110  // Do not schedule a new read if the connection failed.
111  if (bytes_received==0 || err)
112  {
113  if (err==ba::error::eof)
114  {
115  if (!fRdfData.empty())
116  ProcessAnswer();
117  return;
118  }
119 
120  // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
121  // 125: Operation canceled
122  if (err && err!=ba::error::eof && // Connection closed by remote host
123  err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
124  err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
125  {
126  ostringstream str;
127  str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
128  Error(str);
129  }
130  PostClose(err!=ba::error::basic_errors::operation_aborted);
131 
132  fRdfData = "";
133  return;
134  }
135 
136  fRdfData += string(fArray.data(), bytes_received);
137 
138  // Does the message contain a header?
139  const size_t p1 = fRdfData.find("\r\n\r\n");
140  if (p1!=string::npos)
141  {
142  // Does the answer also contain the body?
143  const size_t p2 = fRdfData.find("\r\n\r\n", p1+4);
144  if (p2!=string::npos)
145  ProcessAnswer();
146  }
147 
148  // Go on reading until the web-server closes the connection
149  StartReadReport();
150  }
151 
152  boost::asio::streambuf fBuffer;
153 
155  {
156  async_read_some(ba::buffer(fArray),
157  boost::bind(&ConnectionInterlock::HandleRead, this,
158  dummy::error, dummy::bytes_transferred));
159  }
160 
161  boost::asio::deadline_timer fKeepAlive;
162 
163  void HandleRequest(const bs::error_code &error)
164  {
165  // 125: Operation canceled (bs::error_code(125, bs::system_category))
166  if (error && error!=ba::error::basic_errors::operation_aborted)
167  {
168  ostringstream str;
169  str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
170  Error(str);
171 
172  PostClose(false);
173  return;
174  }
175 
176  if (!is_open())
177  {
178  // For example: Here we could schedule a new accept if we
179  // would not want to allow two connections at the same time.
180  PostClose(true);
181  return;
182  }
183 
184  // Check whether the deadline has passed. We compare the deadline
185  // against the current time since a new asynchronous operation
186  // may have moved the deadline before this actor had a chance
187  // to run.
188  if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
189  return;
190 
191  Request();
192  }
193 
194 
195 private:
196  // This is called when a connection was established
198  {
199  Request();
200  StartReadReport();
201  }
202 
203 public:
204  static const uint16_t kMaxAddr;
205 
206 public:
207  ConnectionInterlock(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
208  fIsValid(false), fIsVerbose(true), fDebugRx(false), fLastReport(Time::none), fKeepAlive(ioservice)
209  {
210  SetLogStream(&imp);
211  }
212 
213  void SetVerbose(bool b)
214  {
215  fIsVerbose = b;
216  }
217 
218  void SetDebugRx(bool b)
219  {
220  fDebugRx = b;
222  }
223 
224  void SetInterval(uint16_t i)
225  {
226  fInterval = i;
227  }
228 
229  void SetSite(const string &site)
230  {
231  fSite = site;
232  }
233 
234  void Post(const string &post)
235  {
236  fNextCommand = post;
237  }
238 
239  void Request()
240  {
241  string cmd = "GET " + fSite;
242 
243  if (!fNextCommand.empty())
244  cmd += "?" + fNextCommand;
245 
246  cmd += " HTTP/1.1\r\n";
247  cmd += "\r\n";
248 
249  PostMessage(cmd);
250 
251  fNextCommand = "";
252 
253  fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval));
254  fKeepAlive.async_wait(boost::bind(&ConnectionInterlock::HandleRequest,
255  this, dummy::error));
256  }
257 
258  int GetInterval() const
259  {
260  return fInterval;
261  }
262 
263  int GetState() const
264  {
265  // Timeout
266  if (!fLastReport.IsValid() || Time()>fLastReport+boost::posix_time::seconds(fInterval*3))
268 
269  // No data received yet
270  if (!fIsValid)
272 
273  /*
274  bool fWaterFlowOk;
275  bool fWaterLevelOk;
276  bool fPwrBiasOn;
277  bool fPwr24VOn;
278  bool fPwrPumpOn;
279  bool fPwrDriveOn;
280  bool fDriveMainSwitchOn;
281  bool fDriveFeedbackOn;
282  */
283 
284  if (!fStatus.fWaterLevelOk || (fStatus.fPwrPumpOn && !fStatus.fWaterFlowOk))
286 
287  const int rc =
288  (fStatus.fPwrBiasOn ? Power::State::kBiasOn : 0) |
289  (fStatus.fPwrPumpOn ? Power::State::kCameraOn : 0) |
291 
292  return rc==0 ? Power::State::kSystemOff : rc;
293  }
294 };
295 
296 const uint16_t ConnectionInterlock::kMaxAddr = 0xfff;
297 
298 // ------------------------------------------------------------------------
299 
300 #include "DimDescriptionService.h"
301 
303 {
304 private:
305  DimDescribedService fDim;
306 
307 public:
308  ConnectionDimWeather(ba::io_service& ioservice, MessageImp &imp) :
309  ConnectionInterlock(ioservice, imp),
310  fDim("PWR_CONTROL/DATA", "C:1;C:1;C:1;C:1;C:1;C:1;C:1;C:1",
311  "|water_lvl[bool]:Water level ok"
312  "|water_flow[bool]:Water flowing"
313  "|pwr_24V[bool]:24V power enabled"
314  "|pwr_pump[bool]:Pump power enabled"
315  "|pwr_bias[bool]:Bias power enabled"
316  "|pwr_drive[bool]:Drive power enabled (command value)"
317  "|main_drive[bool]:Drive manual main switch on"
318  "|feedback_drive[bool]:Drive power on (feedback value)")
319  {
320  }
321 
322  void Update(const Power::Status &status)
323  {
324  fDim.setQuality(status.GetVal());
325  fDim.Update(status);
326  }
327 };
328 
329 // ------------------------------------------------------------------------
330 
331 template <class T, class S>
333 {
334 private:
337 
338  bool CheckEventSize(size_t has, const char *name, size_t size)
339  {
340  if (has==size)
341  return true;
342 
343  ostringstream msg;
344  msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
345  T::Fatal(msg);
346  return false;
347  }
348 
349  int SetVerbosity(const EventImp &evt)
350  {
351  if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
352  return T::kSM_FatalError;
353 
354  fPower.SetVerbose(evt.GetBool());
355 
356  return T::GetCurrentState();
357  }
358 
359  int SetDebugRx(const EventImp &evt)
360  {
361  if (!CheckEventSize(evt.GetSize(), "SetDebugRx", 1))
362  return T::kSM_FatalError;
363 
364  fPower.SetDebugRx(evt.GetBool());
365 
366  return T::GetCurrentState();
367  }
368 
369  int Post(const EventImp &evt)
370  {
371  fPower.Post(evt.GetText());
372  return T::GetCurrentState();
373  }
374 
375  int SetCameraPower(const EventImp &evt)
376  {
377  if (!CheckEventSize(evt.GetSize(), "SetCameraPower", 1))
378  return T::kSM_FatalError;
379 
380  fLastCommand = Time();
381  fPower.Post(evt.GetBool() ? "cam_on=Camera+ON" : "cam_off=Camera+OFF");
382  return T::GetCurrentState();
383  }
384 
386  {
387  fLastCommand = Time();
388  fPower.Post("dt=Drive+ON%2FOFF");
389  return T::GetCurrentState();
390 
391  }
392 
393  int Execute()
394  {
395  const int rc = fPower.GetState();
396 
397  if (rc==Power::State::kCoolingFailure && T::GetCurrentState()!=Power::State::kCoolingFailure)
398  T::Error("Power control unit reported cooling failure.");
399 
400  return fPower.GetState();
401  }
402 
403 
404 public:
405  StateMachinePowerControl(ostream &out=cout) :
406  StateMachineAsio<T>(out, "PWR_CONTROL"), fPower(*this, *this)
407  {
408  // State names
409  T::AddStateName(Power::State::kDisconnected, "NoConnection",
410  "No connection to web-server could be established recently");
411 
412  T::AddStateName(Power::State::kConnected, "Connected",
413  "Connection established, but status still not known");
414 
415  T::AddStateName(Power::State::kSystemOff, "PowerOff",
416  "Camera, Bias and Drive power off");
417 
418  T::AddStateName(Power::State::kBiasOn, "BiasOn",
419  "Camera and Drive power off, Bias on");
420 
421  T::AddStateName(Power::State::kDriveOn, "DriveOn",
422  "Camera and Bias power off, Drive on");
423 
424  T::AddStateName(Power::State::kCameraOn, "CameraOn",
425  "Drive and Bias power off, Camera on");
426 
427  T::AddStateName(Power::State::kBiasOff, "BiasOff",
428  "Camera and Drive power on, Bias off");
429 
430  T::AddStateName(Power::State::kDriveOff, "DriveOff",
431  "Camera and Bias power on, Drive off");
432 
433  T::AddStateName(Power::State::kCameraOff, "CameraOff",
434  "Drive and Bias power on, Camera off");
435 
436  T::AddStateName(Power::State::kSystemOn, "SystemOn",
437  "Camera, Bias and drive power on");
438 
439  T::AddStateName(Power::State::kCoolingFailure, "CoolingFailure",
440  "The cooling unit has failed, the interlock has switched off");
441 
442  // Verbosity commands
443  T::AddEvent("SET_VERBOSE", "B:1")
444  (bind(&StateMachinePowerControl::SetVerbosity, this, placeholders::_1))
445  ("Set verbosity state"
446  "|verbosity[bool]:disable or enable verbosity for interpreted data (yes/no)");
447 
448  T::AddEvent("SET_DEBUG_RX", "B:1")
449  (bind(&StateMachinePowerControl::SetDebugRx, this, placeholders::_1))
450  ("Set debux-rx state"
451  "|debug[bool]:dump received text and parsed text to console (yes/no)");
452 
453  T::AddEvent("CAMERA_POWER", "B:1")
454  (bind(&StateMachinePowerControl::SetCameraPower, this, placeholders::_1))
455  ("Switch camera power"
456  "|power[bool]:Switch camera power 'on' or 'off'");
457 
458  T::AddEvent("TOGGLE_DRIVE")
460  ("Toggle drive power");
461 
462  T::AddEvent("POST", "C")
463  (bind(&StateMachinePowerControl::Post, this, placeholders::_1))
464  ("set verbosity state"
465  "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
466  }
467 
469  {
470  fPower.SetVerbose(!conf.Get<bool>("quiet"));
471  fPower.SetInterval(conf.Get<uint16_t>("interval"));
472  fPower.SetDebugTx(conf.Get<bool>("debug-tx"));
473  fPower.SetDebugRx(conf.Get<bool>("debug-rx"));
474  fPower.SetSite(conf.Get<string>("url"));
475  fPower.SetEndpoint(conf.Get<string>("addr"));
476  fPower.StartConnect();
477 
478  return -1;
479  }
480 };
481 
482 // ------------------------------------------------------------------------
483 
484 #include "Main.h"
485 
486 
487 template<class T, class S, class R>
489 {
490  return Main::execute<T, StateMachinePowerControl<S, R>>(conf);
491 }
492 
494 {
495  po::options_description control("Interlock control");
496  control.add_options()
497  ("no-dim,d", po_switch(), "Disable dim services")
498  ("addr,a", var<string>(""), "Network address of the lid controling Arduino including port")
499  ("url,u", var<string>(""), "File name and path to load")
500  ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
501  ("interval,i", var<uint16_t>(5), "Interval between two updates on the server in seconds")
502  ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
503  ("debug-rx", po_bool(), "Enable debugging for received data.")
504  ;
505 
506  conf.AddOptions(control);
507 }
508 
509 /*
510  Extract usage clause(s) [if any] for SYNOPSIS.
511  Translators: "Usage" and "or" here are patterns (regular expressions) which
512  are used to match the usage synopsis in program output. An example from cp
513  (GNU coreutils) which contains both strings:
514  Usage: cp [OPTION]... [-T] SOURCE DEST
515  or: cp [OPTION]... SOURCE... DIRECTORY
516  or: cp [OPTION]... -t DIRECTORY SOURCE...
517  */
519 {
520  cout <<
521  "The pwrctrl is an interface to the interlock hardware.\n"
522  "\n"
523  "The default is that the program is started without user intercation. "
524  "All actions are supposed to arrive as DimCommands. Using the -c "
525  "option, a local shell can be initialized. With h or help a short "
526  "help message about the usuage can be brought to the screen.\n"
527  "\n"
528  "Usage: pwrctrl [-c type] [OPTIONS]\n"
529  " or: pwrctrl [OPTIONS]\n";
530  cout << endl;
531 }
532 
533 void PrintHelp()
534 {
535 // Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
536 
537  /* Additional help text which is printed after the configuration
538  options goes here */
539 
540  /*
541  cout << "bla bla bla" << endl << endl;
542  cout << endl;
543  cout << "Environment:" << endl;
544  cout << "environment" << endl;
545  cout << endl;
546  cout << "Examples:" << endl;
547  cout << "test exam" << endl;
548  cout << endl;
549  cout << "Files:" << endl;
550  cout << "files" << endl;
551  cout << endl;
552  */
553 }
554 
555 int main(int argc, const char* argv[])
556 {
557  Configuration conf(argv[0]);
560  SetupConfiguration(conf);
561 
562  if (!conf.DoParse(argc, argv, PrintHelp))
563  return 127;
564 
565  // No console access at all
566  if (!conf.Has("console"))
567  {
568  if (conf.Get<bool>("no-dim"))
569  return RunShell<LocalStream, StateMachine, ConnectionInterlock>(conf);
570  else
571  return RunShell<LocalStream, StateMachineDim, ConnectionDimWeather>(conf);
572  }
573  // Cosole access w/ and w/o Dim
574  if (conf.Get<bool>("no-dim"))
575  {
576  if (conf.Get<int>("console")==0)
577  return RunShell<LocalShell, StateMachine, ConnectionInterlock>(conf);
578  else
579  return RunShell<LocalConsole, StateMachine, ConnectionInterlock>(conf);
580  }
581  else
582  {
583  if (conf.Get<int>("console")==0)
584  return RunShell<LocalShell, StateMachineDim, ConnectionDimWeather>(conf);
585  else
586  return RunShell<LocalConsole, StateMachineDim, ConnectionDimWeather>(conf);
587  }
588 
589  return 0;
590 }
uint8_t GetVal() const
Definition: HeadersPower.h:51
StateMachinePowerControl(ostream &out=cout)
Definition: pwrctrl.cc:405
int Post(const EventImp &evt)
Definition: pwrctrl.cc:369
void Print(std::ostream &out, const char *title, const bool &val, const char *t="enabled", const char *f="disabled")
Definition: HeadersPower.cc:56
int RunShell(Configuration &conf)
Definition: pwrctrl.cc:488
bool fWaterLevelOk
Definition: HeadersPower.h:32
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
const char * GetText() const
Definition: EventImp.h:88
void Post(const string &post)
Definition: pwrctrl.cc:234
void SetupConfiguration(Configuration &conf)
Definition: Main.h:25
int i
Definition: db_dim_client.c:21
void setQuality(int quality)
Definition: discpp.cxx:1256
The base implementation of a distributed messaging system.
Definition: MessageImp.h:10
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
char str[80]
Definition: test_client.c:7
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
void HandleRequest(const bs::error_code &error)
Definition: pwrctrl.cc:163
po::typed_value< bool > * po_switch()
STL namespace.
int main(int argc, const char *argv[])
Definition: pwrctrl.cc:555
int SetCameraPower(const EventImp &evt)
Definition: pwrctrl.cc:375
void PrintUsage()
Definition: pwrctrl.cc:518
void HandleRead(const boost::system::error_code &err, size_t bytes_received)
Definition: pwrctrl.cc:108
void SetVerbose(bool b)
Definition: pwrctrl.cc:213
string fNextCommand
Definition: pwrctrl.cc:43
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: pwrctrl.cc:338
bool Has(const std::string &var)
bool fDriveFeedbackOn
Definition: HeadersPower.h:41
ConnectionInterlock(ba::io_service &ioservice, MessageImp &imp)
Definition: pwrctrl.cc:207
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
void SetupConfiguration(Configuration &conf)
Definition: pwrctrl.cc:493
Power::Status fStatus
Definition: pwrctrl.cc:47
void SetSite(const string &site)
Definition: pwrctrl.cc:229
void StartReadReport()
Definition: pwrctrl.cc:154
void Update(const Power::Status &status)
Definition: pwrctrl.cc:322
void SetVerbose(bool b=true)
Definition: Connection.h:148
bool IsValid() const
Definition: Time.h:90
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
boost::asio::streambuf fBuffer
Definition: pwrctrl.cc:152
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
void PrintHelp()
Definition: pwrctrl.cc:533
int size
Definition: db_dim_server.c:17
bool Set(bool &rc, const QString &value)
Definition: HeadersPower.cc:15
void SetDebugRx(bool b)
Definition: pwrctrl.cc:218
bool GetBool() const
Definition: EventImp.h:90
void ProcessAnswer()
Definition: pwrctrl.cc:54
Error()
Definition: HeadersFTM.h:197
boost::array< char, 4096 > fArray
Definition: pwrctrl.cc:41
int GetInterval() const
Definition: pwrctrl.cc:258
int EvalOptions(Configuration &conf)
Definition: pwrctrl.cc:468
po::typed_value< bool > * po_bool(bool def=false)
uint16_t fInterval
Definition: pwrctrl.cc:33
void SetInterval(uint16_t i)
Definition: pwrctrl.cc:224
static const uint16_t kMaxAddr
Definition: pwrctrl.cc:204
int SetVerbosity(const EventImp &evt)
Definition: pwrctrl.cc:349
boost::asio::deadline_timer fKeepAlive
Definition: pwrctrl.cc:161
int SetDebugRx(const EventImp &evt)
Definition: pwrctrl.cc:359
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
void ConnectionEstablished()
Definition: pwrctrl.cc:197
ConnectionDimWeather(ba::io_service &ioservice, MessageImp &imp)
Definition: pwrctrl.cc:308
virtual void Update(const Power::Status &)
Definition: pwrctrl.cc:49
virtual size_t GetSize() const
Definition: EventImp.h:55
int GetState() const
Definition: pwrctrl.cc:263