FACT++  1.0
magiclidar.cc
Go to the documentation of this file.
1 #include <boost/array.hpp>
2 
3 #include "FACT.h"
4 #include "Dim.h"
5 #include "Event.h"
6 #include "Shell.h"
7 #include "StateMachineDim.h"
8 #include "StateMachineAsio.h"
9 #include "Connection.h"
10 #include "LocalControl.h"
11 #include "Configuration.h"
12 #include "Timers.h"
13 #include "Console.h"
14 
15 #include "tools.h"
16 
17 #include "HeadersMagicLidar.h"
18 
19 namespace ba = boost::asio;
20 namespace bs = boost::system;
21 namespace dummy = ba::placeholders;
22 
23 using namespace std;
24 using namespace MagicLidar;
25 
26 // ------------------------------------------------------------------------
27 
28 class ConnectionLidar : public Connection
29 {
30  uint16_t fInterval;
31 
32  bool fIsVerbose;
33 
34  string fSite;
35 
36  virtual void UpdateLidar(const Time &, const DimLidar &)
37  {
38  }
39 
40 protected:
41 
42  boost::array<char, 4096> fArray;
43 
46 
47  void HandleRead(const boost::system::error_code& err, size_t bytes_received)
48  {
49  // Do not schedule a new read if the connection failed.
50  if (bytes_received==0 || err)
51  {
52  if (err==ba::error::eof)
53  Warn("Connection closed by remote host.");
54 
55  // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
56  // 125: Operation canceled
57  if (err && err!=ba::error::eof && // Connection closed by remote host
58  err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
59  err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
60  {
61  ostringstream str;
62  str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
63  Error(str);
64  }
65  PostClose(err!=ba::error::basic_errors::operation_aborted);
66  return;
67  }
68 
69  fLastReception = Time();
70 
71  const string str(fArray.data(), bytes_received);
72  memset(fArray.data(), 0, fArray.size());
73 
74  if (fIsVerbose)
75  Out() << str << endl;
76 
77  bool isheader = true;
78 
79  DimLidar data;
80 
81  int hh=0, mm=0, ss=0, y=0, m=0, d=0;
82 
83  bool keepalive = false;
84  bool failed = false;
85 
86  stringstream is(str);
87  string line;
88  while (getline(is, line))
89  {
90  if (line.size()==1 && line[0]==13)
91  {
92  isheader = false;
93  continue;
94  }
95 
96  if (isheader)
97  {
98  const size_t p = line.find_first_of(": ");
99  if (p==string::npos)
100  continue;
101 
102  std::transform(line.begin(), line.end(), line.begin(), (int(&)(int))std::tolower);
103 
104  const string key = line.substr(0, p);
105  const string val = line.substr(p+2);
106 
107  if (key=="connection" && val=="keep-alive")
108  keepalive = true;
109  }
110  else
111  {
112  try
113  {
114  if (line.substr(0, 2)=="ZD")
115  data.fZd = stoi(line.substr(2));
116  if (line.substr(0, 2)=="AZ")
117  data.fAz = stof(line.substr(2));
118 
119  //if (line.substr(0, 3)=="PBL")
120  // data.fPBL = stof(line.substr(3));
121  //if (line.substr(0, 3)=="CHE")
122  // data.fCHE = stof(line.substr(3));
123  //if (line.substr(0, 3)=="COT")
124  // data.fCOT = stof(line.substr(3));
125 
126  if (line.substr(0, 2)=="T3")
127  data.fT3 = stof(line.substr(2));
128  if (line.substr(0, 2)=="T6")
129  data.fT6 = stof(line.substr(2));
130  if (line.substr(0, 2)=="T9")
131  data.fT9 = stof(line.substr(2));
132  if (line.substr(0, 3)=="T12")
133  data.fT12 = stof(line.substr(3));
134 
135  if (line.substr(0, 3)=="CLB")
136  data.fCloudBaseHeight = stof(line.substr(3));
137 
138  if (line.substr(0, 4)=="HOUR")
139  hh = stoi(line.substr(4));
140  if (line.substr(0, 6)=="MINUTS")
141  mm = stoi(line.substr(6));
142  if (line.substr(0, 7)=="SECONDS")
143  ss = stoi(line.substr(7));
144 
145  if (line.substr(0, 4)=="YEAR")
146  y = stoi(line.substr(4));
147  if (line.substr(0, 5)=="MONTH")
148  m = stoi(line.substr(5));
149  if (line.substr(0, 3)=="DAY")
150  d = stoi(line.substr(3));
151  }
152  catch (const exception &e)
153  {
154  Warn("Conversion of received data failed");
155  failed = true;
156  break;
157  }
158  }
159  }
160 
161  if (!keepalive)
162  PostClose(false);
163 
164  if (failed)
165  return;
166 
167  try
168  {
169  const Time tm = Time(y>999 ? y : 2000+y, m, d, hh, mm, ss);
170  if (tm==fLastReport)
171  return;
172 
173  fLastReport = tm;
174 
175  if (data.fT3==0 && data.fT6==0 && data.fT9==0 && data.fT12==0)
176  return;
177 
178  ostringstream msg;
179  msg << tm.GetAsStr("%H:%M:%S") << ":"
180  //<< " PBL=" << data.fPBL
181  //<< " CHE=" << data.fCHE
182  //<< " COT=" << data.fCOT
183  << " T3-12=" << data.fT3
184  << "/" << data.fT6
185  << "/" << data.fT9
186  << "/" << data.fT12
187  << " H=" << data.fCloudBaseHeight/1000 << "km"
188  << " Zd=" << data.fZd << ""
189  << " Az=" << data.fAz << "";
190  Message(msg);
191 
192  UpdateLidar(tm, data);
193  }
194  catch (const exception &e)
195  {
196  Warn("Corrupted time received.");
197  }
198 
199  }
200 
202  {
203  async_read_some(ba::buffer(fArray),
204  boost::bind(&ConnectionLidar::HandleRead, this,
205  dummy::error, dummy::bytes_transferred));
206  }
207 
208  boost::asio::deadline_timer fKeepAlive;
209 
210  void PostRequest()
211  {
212  const string cmd =
213  "GET "+fSite+" HTTP/1.1\r\n"
214  "Accept: */*\r\n"
215  "Content-Type: application/octet-stream\r\n"
216  "User-Agent: FACT\r\n"
217  "Host: www.fact-project.org\r\n"
218  "Pragma: no-cache\r\n"
219  "Cache-Control: no-cache\r\n"
220  "Expires: 0\r\n"
221  "Connection: Keep-Alive\r\n"
222  "Cache-Control: max-age=0\r\n"
223  "\r\n";
224 
225  PostMessage(cmd);
226  }
227 
228  void Request()
229  {
230  PostRequest();
231 
232  fKeepAlive.expires_from_now(boost::posix_time::seconds(fInterval/2));
233  fKeepAlive.async_wait(boost::bind(&ConnectionLidar::HandleRequest,
234  this, dummy::error));
235  }
236 
237  void HandleRequest(const bs::error_code &error)
238  {
239  // 125: Operation canceled (bs::error_code(125, bs::system_category))
240  if (error && error!=ba::error::basic_errors::operation_aborted)
241  {
242  ostringstream str;
243  str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
244  Error(str);
245 
246  PostClose(false);
247  return;
248  }
249 
250  if (!is_open())
251  {
252  // For example: Here we could schedule a new accept if we
253  // would not want to allow two connections at the same time.
254  PostClose(true);
255  return;
256  }
257 
258  // Check whether the deadline has passed. We compare the deadline
259  // against the current time since a new asynchronous operation
260  // may have moved the deadline before this actor had a chance
261  // to run.
262  if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
263  return;
264 
265  Request();
266  }
267 
268 
269 private:
270  // This is called when a connection was established
272  {
273  Request();
274  StartReadReport();
275  }
276 
277 public:
278 
279  static const uint16_t kMaxAddr;
280 
281 public:
282  ConnectionLidar(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
283  fIsVerbose(true), fLastReport(Time::none), fLastReception(Time::none), fKeepAlive(ioservice)
284  {
285  SetLogStream(&imp);
286  }
287 
288  void SetVerbose(bool b)
289  {
290  fIsVerbose = b;
292  }
293 
294  void SetInterval(uint16_t i)
295  {
296  fInterval = i;
297  }
298 
299  void SetSite(const string &site)
300  {
301  fSite = site;
302  }
303 
304  int GetState() const
305  {
306  if (fLastReport.IsValid() && fLastReport+boost::posix_time::seconds(fInterval*2)>Time())
307  return 3;
308 
309  if (fLastReception.IsValid() && fLastReception+boost::posix_time::seconds(fInterval*2)>Time())
310  return 2;
311 
312  return 1;
313 
314  }
315 };
316 
317 const uint16_t ConnectionLidar::kMaxAddr = 0xfff;
318 
319 // ------------------------------------------------------------------------
320 
321 #include "DimDescriptionService.h"
322 
324 {
325 private:
326 
328 
329  virtual void UpdateLidar(const Time &t, const DimLidar &data)
330  {
331  fDimLidar.setData(&data, sizeof(DimLidar));
332  fDimLidar.Update(t);
333  }
334 
335 public:
336  ConnectionDimLidar(ba::io_service& ioservice, MessageImp &imp) :
337  ConnectionLidar(ioservice, imp),
338  fDimLidar("MAGIC_LIDAR/DATA", "F:1;F:1;F:1;F:1;F:1;F:1;F:1",
339  "|Zd[deg]:Pointing direction zenith distance"
340  "|Az[deg]:Pointing direction azimuth"
341  "|T3[1]:Transmission below 3km normalized to 1"
342  "|T6[1]:Transmission below 6km normalized to 1"
343  "|T9[1]:Transmission below 9km normalized to 1"
344  "|T12[1]:Transmission below 12km normalized to 1"
345  "|CLB[m]:Cloud base height")
346  {
347  }
348 };
349 
350 // ------------------------------------------------------------------------
351 
352 template <class T, class S>
354 {
355 private:
357 
358  bool CheckEventSize(size_t has, const char *name, size_t size)
359  {
360  if (has==size)
361  return true;
362 
363  ostringstream msg;
364  msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
365  T::Fatal(msg);
366  return false;
367  }
368 
369  int SetVerbosity(const EventImp &evt)
370  {
371  if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
372  return T::kSM_FatalError;
373 
374  fLidar.SetVerbose(evt.GetBool());
375 
376  return T::GetCurrentState();
377  }
378 /*
379  int Disconnect()
380  {
381  // Close all connections
382  fLidar.PostClose(false);
383 
384  return T::GetCurrentState();
385  }
386 
387  int Reconnect(const EventImp &evt)
388  {
389  // Close all connections to supress the warning in SetEndpoint
390  fLidar.PostClose(false);
391 
392  // Now wait until all connection have been closed and
393  // all pending handlers have been processed
394  poll();
395 
396  if (evt.GetBool())
397  fLidar.SetEndpoint(evt.GetString());
398 
399  // Now we can reopen the connection
400  fLidar.PostClose(true);
401 
402  return T::GetCurrentState();
403  }
404 */
405  int Execute()
406  {
407  return fLidar.GetState();
408  }
409 
410 
411 public:
412  StateMachineLidar(ostream &out=cout) :
413  StateMachineAsio<T>(out, "MAGIC_LIDAR"), fLidar(*this, *this)
414  {
415  // State names
416  T::AddStateName(State::kDisconnected, "NoConnection",
417  "No connection to web-server could be established recently");
418 
419  T::AddStateName(State::kConnected, "Invalid",
420  "Connection to webserver can be established, but received data is not recent or invalid");
421 
422  T::AddStateName(State::kReceiving, "Valid",
423  "Connection to webserver can be established, receint data received");
424 
425  // Verbosity commands
426  T::AddEvent("SET_VERBOSE", "B")
427  (bind(&StateMachineLidar::SetVerbosity, this, placeholders::_1))
428  ("set verbosity state"
429  "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
430 /*
431  // Conenction commands
432  AddEvent("DISCONNECT")
433  (bind(&StateMachineLidar::Disconnect, this))
434  ("disconnect from ethernet");
435 
436  AddEvent("RECONNECT", "O")
437  (bind(&StateMachineLidar::Reconnect, this, placeholders::_1))
438  ("(Re)connect ethernet connection to FTM, a new address can be given"
439  "|[host][string]:new ethernet address in the form <host:port>");
440 */
441  }
442 
444  {
445  fLidar.SetVerbose(!conf.Get<bool>("quiet"));
446  fLidar.SetInterval(conf.Get<uint16_t>("interval"));
447  fLidar.SetDebugTx(conf.Get<bool>("debug-tx"));
448  fLidar.SetSite(conf.Get<string>("url"));
449  fLidar.SetEndpoint(conf.Get<string>("addr"));
450  fLidar.StartConnect();
451 
452  return -1;
453  }
454 };
455 
456 // ------------------------------------------------------------------------
457 
458 #include "Main.h"
459 
460 
461 template<class T, class S, class R>
463 {
464  return Main::execute<T, StateMachineLidar<S, R>>(conf);
465 }
466 
468 {
469  po::options_description control("MAGIC lidar control options");
470  control.add_options()
471  ("no-dim,d", po_switch(), "Disable dim services")
472  ("addr,a", var<string>("www.magic.iac.es:80"), "Network address of Cosy")
473  ("url,u", var<string>("/site/weather/lidar_data.txt"), "File name and path to load")
474  ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
475  ("interval,i", var<uint16_t>(30), "Interval between two updates on the server in seconds")
476  ("debug-tx", po_bool(), "Enable debugging of ethernet transmission.")
477  ;
478 
479  conf.AddOptions(control);
480 }
481 
482 /*
483  Extract usage clause(s) [if any] for SYNOPSIS.
484  Translators: "Usage" and "or" here are patterns (regular expressions) which
485  are used to match the usage synopsis in program output. An example from cp
486  (GNU coreutils) which contains both strings:
487  Usage: cp [OPTION]... [-T] SOURCE DEST
488  or: cp [OPTION]... SOURCE... DIRECTORY
489  or: cp [OPTION]... -t DIRECTORY SOURCE...
490  */
492 {
493  cout <<
494  "The magiclidar is an interface to the MAGIC lidar data.\n"
495  "\n"
496  "The default is that the program is started without user intercation. "
497  "All actions are supposed to arrive as DimCommands. Using the -c "
498  "option, a local shell can be initialized. With h or help a short "
499  "help message about the usuage can be brought to the screen.\n"
500  "\n"
501  "Usage: magiclidar [-c type] [OPTIONS]\n"
502  " or: magiclidar [OPTIONS]\n";
503  cout << endl;
504 }
505 
506 void PrintHelp()
507 {
508 // Main::PrintHelp<StateMachineFTM<StateMachine, ConnectionFTM>>();
509 
510  /* Additional help text which is printed after the configuration
511  options goes here */
512 
513  /*
514  cout << "bla bla bla" << endl << endl;
515  cout << endl;
516  cout << "Environment:" << endl;
517  cout << "environment" << endl;
518  cout << endl;
519  cout << "Examples:" << endl;
520  cout << "test exam" << endl;
521  cout << endl;
522  cout << "Files:" << endl;
523  cout << "files" << endl;
524  cout << endl;
525  */
526 }
527 
528 int main(int argc, const char* argv[])
529 {
530  Configuration conf(argv[0]);
533  SetupConfiguration(conf);
534 
535  if (!conf.DoParse(argc, argv, PrintHelp))
536  return 127;
537 
538  //try
539  {
540  // No console access at all
541  if (!conf.Has("console"))
542  {
543  if (conf.Get<bool>("no-dim"))
544  return RunShell<LocalStream, StateMachine, ConnectionLidar>(conf);
545  else
546  return RunShell<LocalStream, StateMachineDim, ConnectionDimLidar>(conf);
547  }
548  // Cosole access w/ and w/o Dim
549  if (conf.Get<bool>("no-dim"))
550  {
551  if (conf.Get<int>("console")==0)
552  return RunShell<LocalShell, StateMachine, ConnectionLidar>(conf);
553  else
554  return RunShell<LocalConsole, StateMachine, ConnectionLidar>(conf);
555  }
556  else
557  {
558  if (conf.Get<int>("console")==0)
559  return RunShell<LocalShell, StateMachineDim, ConnectionDimLidar>(conf);
560  else
561  return RunShell<LocalConsole, StateMachineDim, ConnectionDimLidar>(conf);
562  }
563  }
564  /*catch (std::exception& e)
565  {
566  cerr << "Exception: " << e.what() << endl;
567  return -1;
568  }*/
569 
570  return 0;
571 }
void HandleRequest(const bs::error_code &error)
Definition: magiclidar.cc:237
void StartReadReport()
Definition: magiclidar.cc:201
ConnectionLidar(ba::io_service &ioservice, MessageImp &imp)
Definition: magiclidar.cc:282
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: magiclidar.cc:358
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
int EvalOptions(Configuration &conf)
Definition: magiclidar.cc:443
static const uint16_t kMaxAddr
Definition: magiclidar.cc:279
void SetVerbose(bool b)
Definition: magiclidar.cc:288
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
virtual void UpdateLidar(const Time &, const DimLidar &)
Definition: magiclidar.cc:36
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 PrintUsage()
Definition: magiclidar.cc:491
po::typed_value< bool > * po_switch()
STL namespace.
int main(int argc, const char *argv[])
Definition: magiclidar.cc:528
boost::array< char, 4096 > fArray
Definition: magiclidar.cc:42
void SetSite(const string &site)
Definition: magiclidar.cc:299
uint16_t fInterval
Definition: magiclidar.cc:30
void PrintHelp()
Definition: magiclidar.cc:506
bool Has(const std::string &var)
DimDescribedService fDimLidar
Definition: magiclidar.cc:327
ConnectionDimLidar(ba::io_service &ioservice, MessageImp &imp)
Definition: magiclidar.cc:336
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
void setData(const void *ptr, size_t sz)
int GetState() const
Definition: magiclidar.cc:304
boost::asio::deadline_timer fKeepAlive
Definition: magiclidar.cc:208
int RunShell(Configuration &conf)
Definition: magiclidar.cc:462
void SetupConfiguration(Configuration &conf)
Definition: magiclidar.cc:467
void ConnectionEstablished()
Definition: magiclidar.cc:271
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
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
virtual void UpdateLidar(const Time &t, const DimLidar &data)
Definition: magiclidar.cc:329
int SetVerbosity(const EventImp &evt)
Definition: magiclidar.cc:369
int size
Definition: db_dim_server.c:17
void SetInterval(uint16_t i)
Definition: magiclidar.cc:294
float data[4 *1440]
void PostRequest()
Definition: magiclidar.cc:210
bool GetBool() const
Definition: EventImp.h:90
TT t
Definition: test_client.c:26
Error()
Definition: HeadersFTM.h:197
po::typed_value< bool > * po_bool(bool def=false)
std::string GetAsStr(const char *fmt="%Y-%m-%d %H:%M:%S") const
Definition: Time.cc:240
StateMachineLidar(ostream &out=cout)
Definition: magiclidar.cc:412
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
virtual size_t GetSize() const
Definition: EventImp.h:55
void HandleRead(const boost::system::error_code &err, size_t bytes_received)
Definition: magiclidar.cc:47