FACT++  1.0
pfminictrl.cc
Go to the documentation of this file.
1 #include "FACT.h"
2 #include "Dim.h"
3 #include "Event.h"
4 #include "StateMachineDim.h"
5 #include "StateMachineAsio.h"
6 #include "Connection.h"
7 #include "LocalControl.h"
8 #include "Configuration.h"
9 #include "Console.h"
10 
11 #include "tools.h"
12 
13 #include "HeadersPFmini.h"
14 
15 namespace ba = boost::asio;
16 namespace bs = boost::system;
17 namespace dummy = ba::placeholders;
18 
19 using namespace std;
20 
21 class ConnectionPFmini : public Connection
22 {
23 protected:
24  virtual void Update(const PFmini::Data &)
25  {
26  }
27 
28 private:
29  bool fIsVerbose;
30  uint16_t fInterval;
31 
32  bool fReceived;
33 
34  int fState;
35 
36  vector<int16_t> fBuffer;
37 
38  void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
39  {
40  // Do not schedule a new read if the connection failed.
41  if (bytes_received==0 || (err && err!=ba::error::eof))
42  {
43  // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
44  // 125: Operation canceled
45  if (err && err!=ba::error::eof && // Connection closed by remote host
46  err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
47  err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
48  {
49  ostringstream str;
50  str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
51  Error(str);
52  }
53  PostClose(false);
54  return;
55  }
56 
57  const uint16_t chk0 = Tools::Fletcher16(fBuffer.data(), 2);
58  const uint16_t chk1 = uint16_t(fBuffer[2]);
59 
60  if (chk0!=chk1)
61  {
62  ostringstream out;
63  out << "Checksum error (";
64  out << hex << setfill('0');
65  out << setw(4) << fBuffer[0] << ":";
66  out << setw(4) << fBuffer[1] << "|";
67  out << setw(4) << fBuffer[2] << "!=";
68  out << setw(4) << chk1 << ")";
69 
70  Error(out);
71 
72  PostClose(false);
73 
74  return;
75  }
76 
78  data.hum = 110*fBuffer[0]/1024.;
79  data.temp = 110*fBuffer[1]/1024.-20;
80 
81  Update(data);
82 
83  ostringstream msg;
84  msg << fixed << setprecision(1) << "H=" << data.hum << "% T=" << data.temp << "°C" ;
85  Message(msg);
86 
88  fReceived = true;
89  }
90 
91  boost::asio::deadline_timer fKeepAlive;
92 
93  void HandleRequest(const bs::error_code &error)
94  {
95  // 125: Operation canceled (bs::error_code(125, bs::system_category))
96  if (error && error!=ba::error::basic_errors::operation_aborted)
97  {
98  ostringstream str;
99  str << "Timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
100  Error(str);
101 
102  PostClose(false);
103  return;
104  }
105 
106  // Check whether the deadline has passed. We compare the deadline
107  // against the current time since a new asynchronous operation
108  // may have moved the deadline before this actor had a chance
109  // to run.
110  if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
111  return;
112 
113  // Re-open connection
114  PostClose(true);
115  }
116 
117  void HandleReadTimeout(const bs::error_code &error)
118  {
119  // 125: Operation canceled (bs::error_code(125, bs::system_category))
120  if (error && error!=ba::error::basic_errors::operation_aborted)
121  {
122  ostringstream str;
123  str << "Read timeout of " << URL() << " timed out: " << error.message() << " (" << error << ")";// << endl;
124  Error(str);
125 
126  PostClose(false);
127  return;
128  }
129 
130  if (!fReceived)
131  PostClose(false);
132  }
133 
134  void Request()
135  {
136  fReceived = false;
137 
138  string cmd = "GET / HTTP/1.1\r\n\r\n";
139  PostMessage(cmd);
140 
141  fBuffer.resize(6);
142  AsyncRead(ba::buffer(fBuffer));
143  AsyncWait(fInTimeout, 3000, &Connection::HandleReadTimeout);
144 
145  fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval));
146  fKeepAlive.async_wait(boost::bind(&ConnectionPFmini::HandleRequest,
147  this, dummy::error));
148  }
149 
150  // This is called when a connection was established
152  {
153  // Keep state kReceiving
154  if (fState<PFmini::State::kConnected)
155  fState = PFmini::State::kConnected;
156 
157  Request();
158  }
159 
160 public:
161  static const uint16_t kMaxAddr;
162 
163 public:
164  ConnectionPFmini(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
165  fIsVerbose(true), fKeepAlive(ioservice)
166  {
167  SetLogStream(&imp);
168  }
169 
170  void SetVerbose(bool b)
171  {
172  fIsVerbose = b;
174  }
175 
176  void SetInterval(uint16_t i)
177  {
178  fInterval = i;
179  }
180 
181  int GetState() const
182  {
183  if (!is_open())
185 
186  return fState;
187  }
188 };
189 
190 const uint16_t ConnectionPFmini::kMaxAddr = 0xfff;
191 
192 // ------------------------------------------------------------------------
193 
194 #include "DimDescriptionService.h"
195 
197 {
198 private:
199  DimDescribedService fDim;
200 
201 public:
202  ConnectionDimWeather(ba::io_service& ioservice, MessageImp &imp) :
203  ConnectionPFmini(ioservice, imp),
204  fDim("PFMINI_CONTROL/DATA", "F:1;F:1",
205  "Humidity and temperature as read out from the PFmini arduino"
206  "|Humidity[%]:Measures humidity"
207  "|Temperature[deg]:Measured temperature")
208  {
209  }
210 
211  void Update(const PFmini::Data &data)
212  {
213  fDim.Update(data);
214  }
215 };
216 
217 // ------------------------------------------------------------------------
218 
219 template <class T, class S>
221 {
222 private:
225 
226  bool CheckEventSize(size_t has, const char *name, size_t size)
227  {
228  if (has==size)
229  return true;
230 
231  ostringstream msg;
232  msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
233  T::Fatal(msg);
234  return false;
235  }
236 
238  {
239  // Close all connections
240  fPFmini.PostClose(false);
241 
242  return T::GetCurrentState();
243  }
244 
245  int Reconnect(const EventImp &evt)
246  {
247  // Close all connections to supress the warning in SetEndpoint
248  fPFmini.PostClose(false);
249 
250  // Now wait until all connection have been closed and
251  // all pending handlers have been processed
252  ba::io_service::poll();
253 
254  if (evt.GetBool())
255  fPFmini.SetEndpoint(evt.GetString());
256 
257  // Now we can reopen the connection
258  fPFmini.PostClose(true);
259 
260  return T::GetCurrentState();
261  }
262 
263  int SetVerbosity(const EventImp &evt)
264  {
265  if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
266  return T::kSM_FatalError;
267 
268  fPFmini.SetVerbose(evt.GetBool());
269 
270  return T::GetCurrentState();
271  }
272 
273  int Execute()
274  {
275  return fPFmini.GetState();
276  }
277 
278 
279 public:
280  StateMachinePFminiControl(ostream &out=cout) :
281  StateMachineAsio<T>(out, "PFMINI_CONTROL"), fPFmini(*this, *this)
282  {
283  // State names
284  T::AddStateName(PFmini::State::kDisconnected, "Disconnected",
285  "No connection to web-server could be established recently");
286 
287  T::AddStateName(PFmini::State::kConnected, "Connected",
288  "Connection established, but status still not known");
289 
290  T::AddStateName(PFmini::State::kReceiving, "Receiving",
291  "Connection established, receiving reports");
292 
293  // Commands
294  // Verbosity commands
295  T::AddEvent("SET_VERBOSE", "B")
296  (bind(&StateMachinePFminiControl::SetVerbosity, this, placeholders::_1))
297  ("set verbosity state"
298  "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
299 
300  // Conenction commands
301  T::AddEvent("DISCONNECT")
303  ("disconnect from ethernet");
304 
305  T::AddEvent("RECONNECT", "O")
306  (bind(&StateMachinePFminiControl::Reconnect, this, placeholders::_1))
307  ("(Re)connect ethernet connection to PFmini, a new address can be given"
308  "|[host][string]:new ethernet address in the form <host:port>");
309 
310  }
311 
313  {
314  fPFmini.SetVerbose(!conf.Get<bool>("quiet"));
315  fPFmini.SetDebugTx(conf.Get<bool>("debug-tx"));
316  fPFmini.SetEndpoint(conf.Get<string>("addr"));
317  fPFmini.SetInterval(conf.Get<uint16_t>("interval"));
318  fPFmini.StartConnect();
319 
320  return -1;
321  }
322 };
323 
324 // ------------------------------------------------------------------------
325 
326 #include "Main.h"
327 
328 
329 template<class T, class S, class R>
331 {
332  return Main::execute<T, StateMachinePFminiControl<S, R>>(conf);
333 }
334 
336 {
337  po::options_description control("PFmini control");
338  control.add_options()
339  ("no-dim,d", po_switch(), "Disable dim services")
340  ("addr,a", var<string>("10.0.130.140:80"), "Network address of the lid controling Arduino including port")
341  ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
342  ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
343  ("interval", var<uint16_t>(15), "Interval in seconds at which a report is requested.")
344  ;
345 
346  conf.AddOptions(control);
347 }
348 
349 /*
350  Extract usage clause(s) [if any] for SYNOPSIS.
351  Translators: "Usage" and "or" here are patterns (regular expressions) which
352  are used to match the usage synopsis in program output. An example from cp
353  (GNU coreutils) which contains both strings:
354  Usage: cp [OPTION]... [-T] SOURCE DEST
355  or: cp [OPTION]... SOURCE... DIRECTORY
356  or: cp [OPTION]... -t DIRECTORY SOURCE...
357  */
359 {
360  cout <<
361  "The pfminictrl is an interface to the PFmini arduino.\n"
362  "\n"
363  "The default is that the program is started without user intercation. "
364  "All actions are supposed to arrive as DimCommands. Using the -c "
365  "option, a local shell can be initialized. With h or help a short "
366  "help message about the usuage can be brought to the screen.\n"
367  "\n"
368  "Usage: pfminictrlö [-c type] [OPTIONS]\n"
369  " or: pfminictrl [OPTIONS]\n";
370  cout << endl;
371 }
372 
373 void PrintHelp()
374 {
375 // Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
376 
377  /* Additional help text which is printed after the configuration
378  options goes here */
379 
380  /*
381  cout << "bla bla bla" << endl << endl;
382  cout << endl;
383  cout << "Environment:" << endl;
384  cout << "environment" << endl;
385  cout << endl;
386  cout << "Examples:" << endl;
387  cout << "test exam" << endl;
388  cout << endl;
389  cout << "Files:" << endl;
390  cout << "files" << endl;
391  cout << endl;
392  */
393 }
394 
395 int main(int argc, const char* argv[])
396 {
397  Configuration conf(argv[0]);
400  SetupConfiguration(conf);
401 
402  if (!conf.DoParse(argc, argv, PrintHelp))
403  return 127;
404 
405  // No console access at all
406  if (!conf.Has("console"))
407  {
408  if (conf.Get<bool>("no-dim"))
409  return RunShell<LocalStream, StateMachine, ConnectionPFmini>(conf);
410  else
411  return RunShell<LocalStream, StateMachineDim, ConnectionDimWeather>(conf);
412  }
413  // Cosole access w/ and w/o Dim
414  if (conf.Get<bool>("no-dim"))
415  {
416  if (conf.Get<int>("console")==0)
417  return RunShell<LocalShell, StateMachine, ConnectionPFmini>(conf);
418  else
419  return RunShell<LocalConsole, StateMachine, ConnectionPFmini>(conf);
420  }
421  else
422  {
423  if (conf.Get<int>("console")==0)
424  return RunShell<LocalShell, StateMachineDim, ConnectionDimWeather>(conf);
425  else
426  return RunShell<LocalConsole, StateMachineDim, ConnectionDimWeather>(conf);
427  }
428 
429  return 0;
430 }
virtual void Update(const PFmini::Data &)
Definition: pfminictrl.cc:24
int SetVerbosity(const EventImp &evt)
Definition: pfminictrl.cc:263
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
uint16_t fInterval
Definition: pfminictrl.cc:30
void SetupConfiguration(Configuration &conf)
Definition: Main.h:25
void HandleRequest(const bs::error_code &error)
Definition: pfminictrl.cc:93
int i
Definition: db_dim_client.c:21
The base implementation of a distributed messaging system.
Definition: MessageImp.h:10
StateMachinePFminiControl(ostream &out=cout)
Definition: pfminictrl.cc:280
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 Update(const PFmini::Data &data)
Definition: pfminictrl.cc:211
po::typed_value< bool > * po_switch()
STL namespace.
static const uint16_t kMaxAddr
Definition: pfminictrl.cc:161
uint16_t fState
State of the FTM central state machine.
Definition: HeadersFTM.h:189
uint16_t Fletcher16(const T *t, size_t cnt)
Definition: tools.h:22
std::string GetString() const
Definition: EventImp.cc:194
int main(int argc, const char *argv[])
Definition: pfminictrl.cc:395
int GetState() const
Definition: pfminictrl.cc:181
boost::asio::deadline_timer fKeepAlive
Definition: pfminictrl.cc:91
void SetVerbose(bool b)
Definition: pfminictrl.cc:170
void SetupConfiguration(Configuration &conf)
Definition: pfminictrl.cc:335
bool Has(const std::string &var)
int Reconnect(const EventImp &evt)
Definition: pfminictrl.cc:245
void SetInterval(uint16_t i)
Definition: pfminictrl.cc:176
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
void PrintUsage()
Definition: pfminictrl.cc:358
void ConnectionEstablished()
Definition: pfminictrl.cc:151
void PrintHelp()
Definition: pfminictrl.cc:373
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: pfminictrl.cc:226
void SetVerbose(bool b=true)
Definition: Connection.h:148
void HandleReadTimeout(const bs::error_code &error)
Definition: pfminictrl.cc:117
vector< int16_t > fBuffer
Definition: pfminictrl.cc:36
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
int size
Definition: db_dim_server.c:17
float data[4 *1440]
void HandleReceivedData(const bs::error_code &err, size_t bytes_received, int)
Definition: pfminictrl.cc:38
virtual void HandleReadTimeout(const boost::system::error_code &)
Definition: Connection.h:138
bool GetBool() const
Definition: EventImp.h:90
Error()
Definition: HeadersFTM.h:197
int EvalOptions(Configuration &conf)
Definition: pfminictrl.cc:312
po::typed_value< bool > * po_bool(bool def=false)
ConnectionPFmini(ba::io_service &ioservice, MessageImp &imp)
Definition: pfminictrl.cc:164
int RunShell(Configuration &conf)
Definition: pfminictrl.cc:330
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
ConnectionDimWeather(ba::io_service &ioservice, MessageImp &imp)
Definition: pfminictrl.cc:202
virtual size_t GetSize() const
Definition: EventImp.h:55