FACT++  1.0
temperature.cc
Go to the documentation of this file.
1 #if BOOST_VERSION < 104600
2 #include <assert.h>
3 #endif
4 
5 #include <boost/array.hpp>
6 
7 #include <boost/property_tree/ptree.hpp>
8 #include <boost/property_tree/json_parser.hpp>
9 
10 #include <string>
11 
12 #include "FACT.h"
13 #include "Dim.h"
14 #include "Event.h"
15 #include "StateMachineDim.h"
16 #include "StateMachineAsio.h"
17 #include "Connection.h"
18 #include "LocalControl.h"
19 #include "Configuration.h"
20 #include "Console.h"
21 
22 #include "tools.h"
23 
24 #include "HeadersTemperature.h"
25 
26 namespace ba = boost::asio;
27 namespace bs = boost::system;
28 namespace pt = boost::property_tree;
29 namespace dummy = ba::placeholders;
30 
31 using namespace std;
32 
34 {
35 protected:
36  bool fIsValid;
37 
38 private:
39  uint16_t fInterval;
40 
41  bool fIsVerbose;
42  bool fDebugRx;
43 
44  string fSite;
45  string fRdfData;
46 
47  boost::array<char, 4096> fArray;
48 
49  string fNextCommand;
50 
52 
53  int fStatus;
54 
55  virtual void Update(const vector<float> &)
56  {
57  }
58 
59 
61  {
62  if (fDebugRx)
63  {
64  Out() << "------------------------------------------------------" << endl;
65  Out() << fRdfData << endl;
66  Out() << "------------------------------------------------------" << endl;
67  }
68 
69  const size_t p1 = fRdfData.find("\r\n\r\n");
70  if (p1==string::npos)
71  {
72  Warn("HTTP header not found.");
73  PostClose(false);
74  return;
75  }
76 
77  fRdfData.erase(0, p1+4);
78 
79  vector<float> temp(3);
80  try
81  {
82  std::stringstream ss;
83  ss << fRdfData;
84 
85  pt::ptree tree;
86  pt::read_json(ss, tree);
87 
88  const pt::ptree sub2 = tree.get_child("sensor_values.").begin()->second;
89  const pt::ptree sub3 = sub2.get_child("values").begin()->second.begin()->second;
90 
91  temp[0] = sub3.get_child("v").get_value<float>();
92 
93  auto sub = sub3.get_child("st.").begin();
94 
95  temp[1] = sub++->second.get_value<float>();
96  temp[2] = sub->second.get_value<float>();
97  }
98  catch (std::exception const& e)
99  {
100  Warn("Parsing of JSON failed: "+string(e.what()));
101 
103 
104  PostClose(false);
105  return;
106  }
107 
108  fRdfData = "";
109 
110  Update(temp);
111 
112  ostringstream msg;
113  msg << "T=" << temp[0] << "°C"
114  << " Tmin=" << temp[1] << "°C"
115  << " Tmax=" << temp[2] << "°C";
116  Message(msg);
117 
118  fStatus = Temperature::State::kValid;
119 
120  fLastReport = Time();
121  PostClose(false);
122  }
123 
124  void HandleRead(const boost::system::error_code& err, size_t bytes_received)
125  {
126  // Do not schedule a new read if the connection failed.
127  if (bytes_received==0 || err)
128  {
129  if (err==ba::error::eof)
130  {
131  if (!fRdfData.empty())
132  ProcessAnswer();
133  return;
134  }
135 
136  // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
137  // 125: Operation canceled
138  if (err && err!=ba::error::eof && // Connection closed by remote host
139  err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
140  err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
141  {
142  ostringstream str;
143  str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
144  Error(str);
145  }
146  PostClose(err!=ba::error::basic_errors::operation_aborted);
147 
148  fRdfData = "";
149  return;
150  }
151 
152  fRdfData += string(fArray.data(), bytes_received);
153 
154  // Does the message contain a header?
155  const size_t p1 = fRdfData.find("\r\n\r\n");
156  if (p1!=string::npos)
157  {
158  // Does the answer also contain the body?
159  const size_t p2 = fRdfData.find("\r\n\r\n", p1+4);
160  if (p2!=string::npos)
161  ProcessAnswer();
162  }
163 
164  // Go on reading until the web-server closes the connection
165  StartReadReport();
166  }
167 
168  boost::asio::streambuf fBuffer;
169 
171  {
172  async_read_some(ba::buffer(fArray),
173  boost::bind(&ConnectionPowerSwitch::HandleRead, this,
174  dummy::error, dummy::bytes_transferred));
175  }
176 
177  boost::asio::deadline_timer fKeepAlive;
178 
179  void HandleRequest(const bs::error_code &error)
180  {
181  // 125: Operation canceled (bs::error_code(125, bs::system_category))
182  if (error && error!=ba::error::basic_errors::operation_aborted)
183  {
184  ostringstream str;
185  str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
186  Error(str);
187 
188  PostClose(false);
189  return;
190  }
191 
192  if (!is_open())
193  {
194  // For example: Here we could schedule a new accept if we
195  // would not want to allow two connections at the same time.
196  PostClose(true);
197  return;
198  }
199 
200  // Check whether the deadline has passed. We compare the deadline
201  // against the current time since a new asynchronous operation
202  // may have moved the deadline before this actor had a chance
203  // to run.
204  if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
205  return;
206 
207  Request();
208  }
209 
210 
211 private:
212  // This is called when a connection was established
214  {
215  Request();
216  StartReadReport();
217  }
218 
219 public:
220  static const uint16_t kMaxAddr;
221 
222 public:
223  ConnectionPowerSwitch(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
224  fIsValid(false), fIsVerbose(true), fDebugRx(false), fLastReport(Time::none),
225  fStatus(Temperature::State::kDisconnected), fKeepAlive(ioservice)
226  {
227  SetLogStream(&imp);
228  }
229 
230  void SetVerbose(bool b)
231  {
232  fIsVerbose = b;
233  }
234 
235  void SetDebugRx(bool b)
236  {
237  fDebugRx = b;
239  }
240 
241  void SetInterval(uint16_t i)
242  {
243  fInterval = i;
244  }
245 
246  void SetSite(const string &site)
247  {
248  fSite = site;
249  }
250 
251  void Post(const string &post)
252  {
253  fNextCommand = post;
254  }
255 
256  void Request()
257  {
258  string cmd = "GET " + fSite;
259 
260  if (!fNextCommand.empty())
261  cmd += "?" + fNextCommand;
262 
263  cmd += " HTTP/1.1\r\n";
264  cmd += "\r\n";
265 
266  PostMessage(cmd);
267 
268  fNextCommand = "";
269 
270  fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval));
271  fKeepAlive.async_wait(boost::bind(&ConnectionPowerSwitch::HandleRequest,
272  this, dummy::error));
273  }
274 
275  int GetInterval() const
276  {
277  return fInterval;
278  }
279 
280  int GetState() const
281  {
282  // Timeout
283  if (!fLastReport.IsValid() || Time()>fLastReport+boost::posix_time::seconds(fInterval*3))
285 
286  return fStatus;
287  }
288 };
289 
290 const uint16_t ConnectionPowerSwitch::kMaxAddr = 0xfff;
291 
292 // ------------------------------------------------------------------------
293 
294 #include "DimDescriptionService.h"
295 
297 {
298 private:
300 
301 public:
302  ConnectionDimPowerSwitch(ba::io_service& ioservice, MessageImp &imp) :
303  ConnectionPowerSwitch(ioservice, imp),
304  fDim("TEMPERATURE/DATA", "F:1;F:1;F:1",
305  "Temperature readout from power switch"
306  "|T[degC]:Current temperature"
307  "|Tmin[degC]:24h minimum"
308  "|Tmax[degC]:24h maximum")
309  {
310  }
311 
312  void Update(const vector<float> &temp)
313  {
314  fDim.Update(temp);
315  }
316 };
317 
318 // ------------------------------------------------------------------------
319 
320 template <class T, class S>
322 {
323 private:
324  S fPower;
325  Time fLastCommand;
326 
327  bool CheckEventSize(size_t has, const char *name, size_t size)
328  {
329  if (has==size)
330  return true;
331 
332  ostringstream msg;
333  msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
334  T::Fatal(msg);
335  return false;
336  }
337 
338  int SetVerbosity(const EventImp &evt)
339  {
340  if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
341  return T::kSM_FatalError;
342 
343  fPower.SetVerbose(evt.GetBool());
344 
345  return T::GetCurrentState();
346  }
347 
348  int SetDebugRx(const EventImp &evt)
349  {
350  if (!CheckEventSize(evt.GetSize(), "SetDebugRx", 1))
351  return T::kSM_FatalError;
352 
353  fPower.SetDebugRx(evt.GetBool());
354 
355  return T::GetCurrentState();
356  }
357 
358  int Execute()
359  {
360  return fPower.GetState();
361  }
362 
363 
364 public:
365  StateMachinePowerControl(ostream &out=cout) :
366  StateMachineAsio<T>(out, "TEMPERATURE"), fPower(*this, *this)
367  {
368  // State names
369  T::AddStateName(Temperature::State::kDisconnected, "NoConnection",
370  "No connection to web-server could be established recently");
371 
372  T::AddStateName(Temperature::State::kConnected, "Connected",
373  "Connection established, but no valid data received");
374 
375  T::AddStateName(Temperature::State::kValid, "Valid",
376  "Connection established, received data valid");
377 
378  // Verbosity commands
379  T::AddEvent("SET_VERBOSE", "B:1")
380  (bind(&StateMachinePowerControl::SetVerbosity, this, placeholders::_1))
381  ("Set verbosity state"
382  "|verbosity[bool]:disable or enable verbosity for interpreted data (yes/no)");
383 
384  T::AddEvent("SET_DEBUG_RX", "B:1")
385  (bind(&StateMachinePowerControl::SetDebugRx, this, placeholders::_1))
386  ("Set debux-rx state"
387  "|debug[bool]:dump received text and parsed text to console (yes/no)");
388 
389  }
390 
392  {
393  fPower.SetVerbose(!conf.Get<bool>("quiet"));
394  fPower.SetInterval(conf.Get<uint16_t>("interval"));
395  fPower.SetDebugTx(conf.Get<bool>("debug-tx"));
396  fPower.SetDebugRx(conf.Get<bool>("debug-rx"));
397  fPower.SetSite(conf.Get<string>("url"));
398  fPower.SetEndpoint(conf.Get<string>("addr"));
399  fPower.StartConnect();
400 
401  return -1;
402  }
403 };
404 
405 // ------------------------------------------------------------------------
406 
407 #include "Main.h"
408 
409 
410 template<class T, class S, class R>
412 {
413  return Main::execute<T, StateMachinePowerControl<S, R>>(conf);
414 }
415 
417 {
418  po::options_description control("Lid control");
419  control.add_options()
420  ("no-dim,d", po_switch(), "Disable dim services")
421  ("addr,a", var<string>("10.0.100.234:80"), "Network address of the lid controling Arduino including port")
422  ("url,u", var<string>("/statusjsn.js?components=18179&_=1365876572736"), "File name and path to load")
423  ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
424  ("interval,i", var<uint16_t>(60), "Interval between two updates on the server in seconds")
425  ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
426  ("debug-rx", po_bool(), "Enable debugging for received data.")
427  ;
428 
429  conf.AddOptions(control);
430 }
431 
432 /*
433  Extract usage clause(s) [if any] for SYNOPSIS.
434  Translators: "Usage" and "or" here are patterns (regular expressions) which
435  are used to match the usage synopsis in program output. An example from cp
436  (GNU coreutils) which contains both strings:
437  Usage: cp [OPTION]... [-T] SOURCE DEST
438  or: cp [OPTION]... SOURCE... DIRECTORY
439  or: cp [OPTION]... -t DIRECTORY SOURCE...
440  */
442 {
443  cout <<
444  "The temperature is an interface to readout the temperature from the power switch.\n"
445  "\n"
446  "The default is that the program is started without user intercation. "
447  "All actions are supposed to arrive as DimCommands. Using the -c "
448  "option, a local shell can be initialized. With h or help a short "
449  "help message about the usuage can be brought to the screen.\n"
450  "\n"
451  "Usage: temperature [-c type] [OPTIONS]\n"
452  " or: temperature [OPTIONS]\n";
453  cout << endl;
454 }
455 
456 void PrintHelp()
457 {
458 // Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
459 
460  /* Additional help text which is printed after the configuration
461  options goes here */
462 
463  /*
464  cout << "bla bla bla" << endl << endl;
465  cout << endl;
466  cout << "Environment:" << endl;
467  cout << "environment" << endl;
468  cout << endl;
469  cout << "Examples:" << endl;
470  cout << "test exam" << endl;
471  cout << endl;
472  cout << "Files:" << endl;
473  cout << "files" << endl;
474  cout << endl;
475  */
476 }
477 
478 int main(int argc, const char* argv[])
479 {
480  Configuration conf(argv[0]);
483  SetupConfiguration(conf);
484 
485  if (!conf.DoParse(argc, argv, PrintHelp))
486  return 127;
487 
488  // No console access at all
489  if (!conf.Has("console"))
490  {
491  if (conf.Get<bool>("no-dim"))
492  return RunShell<LocalStream, StateMachine, ConnectionPowerSwitch>(conf);
493  else
494  return RunShell<LocalStream, StateMachineDim, ConnectionDimPowerSwitch>(conf);
495  }
496  // Cosole access w/ and w/o Dim
497  if (conf.Get<bool>("no-dim"))
498  {
499  if (conf.Get<int>("console")==0)
500  return RunShell<LocalShell, StateMachine, ConnectionPowerSwitch>(conf);
501  else
502  return RunShell<LocalConsole, StateMachine, ConnectionPowerSwitch>(conf);
503  }
504  else
505  {
506  if (conf.Get<int>("console")==0)
507  return RunShell<LocalShell, StateMachineDim, ConnectionDimPowerSwitch>(conf);
508  else
509  return RunShell<LocalConsole, StateMachineDim, ConnectionDimPowerSwitch>(conf);
510  }
511 
512  return 0;
513 }
StateMachinePowerControl(ostream &out=cout)
Definition: temperature.cc:365
boost::asio::streambuf fBuffer
Definition: temperature.cc:168
int RunShell(Configuration &conf)
Definition: temperature.cc:411
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
uint16_t fStatus
Definition: HeadersFAD.h:173
void SetupConfiguration(Configuration &conf)
Definition: Main.h:25
int i
Definition: db_dim_client.c:21
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)
int GetState() const
Definition: temperature.cc:280
int GetInterval() const
Definition: temperature.cc:275
DimDescribedService fDim
Definition: temperature.cc:299
po::typed_value< bool > * po_switch()
STL namespace.
void PrintUsage()
Definition: temperature.cc:441
float temp
Definition: HeadersPFmini.h:56
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: temperature.cc:327
void HandleRequest(const bs::error_code &error)
Definition: temperature.cc:179
bool Has(const std::string &var)
void SetSite(const string &site)
Definition: temperature.cc:246
void Update(const vector< float > &temp)
Definition: temperature.cc:312
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
void SetInterval(uint16_t i)
Definition: temperature.cc:241
void PrintHelp()
Definition: temperature.cc:456
void SetVerbose(bool b=true)
Definition: Connection.h:148
bool IsValid() const
Definition: Time.h:90
void SetupConfiguration(Configuration &conf)
Definition: temperature.cc:416
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
boost::array< char, 4096 > fArray
Definition: temperature.cc:47
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
void Post(const string &post)
Definition: temperature.cc:251
int size
Definition: db_dim_server.c:17
bool GetBool() const
Definition: EventImp.h:90
virtual void Update(const vector< float > &)
Definition: temperature.cc:55
Error()
Definition: HeadersFTM.h:197
int EvalOptions(Configuration &conf)
Definition: temperature.cc:391
po::typed_value< bool > * po_bool(bool def=false)
void SetDebugRx(bool b)
Definition: temperature.cc:235
ConnectionDimPowerSwitch(ba::io_service &ioservice, MessageImp &imp)
Definition: temperature.cc:302
int SetVerbosity(const EventImp &evt)
Definition: temperature.cc:338
void SetVerbose(bool b)
Definition: temperature.cc:230
void HandleRead(const boost::system::error_code &err, size_t bytes_received)
Definition: temperature.cc:124
Maintains an ansynchronous TCP/IP client connection.
Definition: Connection.h:15
boost::asio::deadline_timer fKeepAlive
Definition: temperature.cc:177
int SetDebugRx(const EventImp &evt)
Definition: temperature.cc:348
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
int main(int argc, const char *argv[])
Definition: temperature.cc:478
static const uint16_t kMaxAddr
Definition: temperature.cc:220
ConnectionPowerSwitch(ba::io_service &ioservice, MessageImp &imp)
Definition: temperature.cc:223
virtual size_t GetSize() const
Definition: EventImp.h:55