source: trunk/FACT++/src/magicweather.cc@ 12954

Last change on this file since 12954 was 12954, checked in by tbretz, 13 years ago
Added include of LocalControl.h
File size: 15.9 KB
Line 
1#include <boost/bind.hpp>
2
3#include "FACT.h"
4#include "Dim.h"
5#include "Event.h"
6#include "Shell.h"
7#include "StateMachineDim.h"
8#include "Connection.h"
9#include "LocalControl.h"
10#include "Configuration.h"
11#include "Timers.h"
12#include "Console.h"
13#include "Converter.h"
14
15#include "tools.h"
16
17
18namespace ba = boost::asio;
19namespace bs = boost::system;
20namespace dummy = ba::placeholders;
21
22using namespace std;
23
24// ------------------------------------------------------------------------
25
26struct DimWeather
27{
28 DimWeather() { memset(this, 0, sizeof(DimWeather)); }
29
30 uint16_t fStatus;
31
32 float fTemp;
33 float fDew;
34 float fHum;
35 float fPress;
36 float fWind;
37 float fDir;
38
39} __attribute__((__packed__));
40
41
42// ------------------------------------------------------------------------
43
44class ConnectionWeather : public Connection
45{
46 int fState;
47
48 bool fIsVerbose;
49
50 virtual void UpdateWeather(const Time &, const DimWeather &)
51 {
52 }
53
54protected:
55
56 boost::array<char, 4096> fArray;
57
58 Time fLastReport;
59
60 void HandleRead(const boost::system::error_code& err, size_t bytes_received)
61 {
62 // Do not schedule a new read if the connection failed.
63 if (bytes_received==0 || err)
64 {
65 if (err==ba::error::eof)
66 Warn("Connection closed by remote host.");
67
68 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
69 // 125: Operation canceled
70 if (err && err!=ba::error::eof && // Connection closed by remote host
71 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
72 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
73 {
74 ostringstream str;
75 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
76 Error(str);
77 }
78 PostClose(err!=ba::error::basic_errors::operation_aborted);
79 return;
80 }
81
82 PostClose(false);
83
84 const string str(fArray.data(), bytes_received);
85 memset(fArray.data(), 0, fArray.size());
86
87 if (fIsVerbose)
88 Out() << str << endl;
89
90 bool isheader = true;
91
92 DimWeather data;
93
94 int hh=0, mm=0, ss=0, y=0, m=0, d=0;
95
96 stringstream is(str);
97 string line;
98 while (getline(is, line))
99 {
100 if (line.size()==1 && line[0]==13)
101 {
102 isheader = false;
103 continue;
104 }
105
106 if (isheader)
107 {
108 /*
109 if (line.substr(0, 16)=="Content-Length: ")
110 {
111 fSize = atoi(line.substr(16).data());
112 cout << "Size==" << fSize << endl;
113 }
114
115 if (line.substr(0, 15)=="Last-Modified: ")
116 cout << "Last==" << line.substr(15).data() << endl;
117 */
118 }
119 else
120 {
121 if (line.substr(0, 2)=="ST")
122 data.fStatus = atoi(line.substr(2).data());
123
124 if (line.substr(0, 2)=="TE")
125 data.fTemp = atof(line.substr(2).data());
126
127 if (line.substr(0, 2)=="DP")
128 data.fDew = atof(line.substr(2).data());
129
130 if (line.substr(0, 3)=="HUM")
131 data.fHum = atof(line.substr(3).data());
132
133 if (line.substr(0, 2)=="WS")
134 data.fWind = atof(line.substr(2).data());
135
136 if (line.substr(0, 3)=="MWD")
137 data.fDir = atof(line.substr(3).data());
138
139 if (line.substr(0, 5)=="PRESS")
140 data.fPress = atof(line.substr(5).data());
141
142 if (line.substr(0, 4)=="HOUR")
143 hh = atoi(line.substr(4).data());
144
145 if (line.substr(0, 6)=="MINUTS")
146 mm = atoi(line.substr(6).data());
147
148 if (line.substr(0, 7)=="SECONDS")
149 ss = atoi(line.substr(7).data());
150
151 if (line.substr(0, 4)=="YEAR")
152 y = atoi(line.substr(4).data());
153
154 if (line.substr(0, 5)=="MONTH")
155 m = atoi(line.substr(5).data());
156
157 if (line.substr(0, 3)=="DAY")
158 d = atoi(line.substr(3).data());
159 }
160 }
161
162 try
163 {
164 const Time tm = Time(2000+y, m, d, hh, mm, ss);
165 if (tm==fLastReport)
166 return;
167
168 ostringstream msg;
169 msg << tm << "[" << data.fStatus << "]: T="
170 << data.fTemp << "°C H=" << data.fHum << "% P="
171 << data.fPress << "hPa Td=" << data.fDew << "°C V="
172 << data.fWind << "km/h dir=" << data.fDir << "deg" << endl;
173 Message(msg);
174
175 UpdateWeather(tm, data);
176
177 fLastReport = tm;
178 }
179 catch (const exception &e)
180 {
181 Warn("Corrupted time received.");
182 }
183
184 }
185
186 void StartReadReport()
187 {
188 async_read_some(ba::buffer(fArray),
189 boost::bind(&ConnectionWeather::HandleRead, this,
190 dummy::error, dummy::bytes_transferred));
191 }
192
193 boost::asio::deadline_timer fKeepAlive;
194
195 void PostRequest()
196 {
197 const string dir = "/site/weather/weather_data.txt";
198
199 const string cmd = "GET "+dir+" HTTP/1.1\r\nHost: www.fact-project.org\r\n\r\n";
200 PostMessage(cmd);
201 }
202
203 void Request()
204 {
205 PostRequest();
206
207 fKeepAlive.expires_from_now(boost::posix_time::seconds(55));
208 fKeepAlive.async_wait(boost::bind(&ConnectionWeather::HandleRequest,
209 this, dummy::error));
210 }
211
212 void HandleRequest(const bs::error_code &error)
213 {
214 // 125: Operation canceled (bs::error_code(125, bs::system_category))
215 if (error && error!=ba::error::basic_errors::operation_aborted)
216 {
217 ostringstream str;
218 str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
219 Error(str);
220
221 PostClose(false);
222 return;
223 }
224
225 if (!is_open())
226 {
227 // For example: Here we could schedule a new accept if we
228 // would not want to allow two connections at the same time.
229 PostClose(true);
230 return;
231 }
232
233 // Check whether the deadline has passed. We compare the deadline
234 // against the current time since a new asynchronous operation
235 // may have moved the deadline before this actor had a chance
236 // to run.
237 if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
238 return;
239
240 Request();
241 }
242
243
244private:
245 // This is called when a connection was established
246 void ConnectionEstablished()
247 {
248 Request();
249 StartReadReport();
250 }
251
252public:
253
254 static const uint16_t kMaxAddr;
255
256public:
257 ConnectionWeather(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
258 fState(-1), fIsVerbose(true), fKeepAlive(ioservice)
259 {
260 SetLogStream(&imp);
261 }
262
263 void SetVerbose(bool b)
264 {
265 fIsVerbose = b;
266 }
267
268 int GetState() const
269 {
270 if (!IsConnected())
271 return 1;
272 if (IsConnected() && fState<0)
273 return 2;
274 return fState+3;
275 }
276};
277
278const uint16_t ConnectionWeather::kMaxAddr = 0xfff;
279
280// ------------------------------------------------------------------------
281
282#include "DimDescriptionService.h"
283
284class ConnectionDimDrive : public ConnectionWeather
285{
286private:
287
288 DimDescribedService fDimWeather;
289
290 virtual void UpdateWeather(const Time &t, const DimWeather &data)
291 {
292 fDimWeather.setData(&data, sizeof(DimWeather));
293 fDimWeather.Update(t);
294 }
295
296public:
297 ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) :
298 ConnectionWeather(ioservice, imp),
299 fDimWeather("MAGIC_WEATHER/DATA", "S:1;F:1;F:1;F:1;F:1;F:1;F:1",
300 "|stat:Status"
301 "|T[deg C]:Temperature"
302 "|T_dew[deg C]:Dew point"
303 "|H[%]:Humidity"
304 "|P[hPa]:Air pressure"
305 "|v[km/h]:Wind speed"
306 "|d[deg]:Wind direction (N-E)")
307 {
308 }
309};
310
311// ------------------------------------------------------------------------
312
313template <class T, class S>
314class StateMachineDrive : public T, public ba::io_service, public ba::io_service::work
315{
316 int Wrap(boost::function<void()> f)
317 {
318 f();
319 return T::GetCurrentState();
320 }
321
322 boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
323 {
324 return bind(&StateMachineDrive::Wrap, this, func);
325 }
326
327private:
328 S fWeather;
329
330 enum states_t
331 {
332 kStateDisconnected = 1,
333 kStateConnected,
334 kStateOk,
335 };
336
337 bool CheckEventSize(size_t has, const char *name, size_t size)
338 {
339 if (has==size)
340 return true;
341
342 ostringstream msg;
343 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
344 T::Fatal(msg);
345 return false;
346 }
347
348 int SetVerbosity(const EventImp &evt)
349 {
350 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
351 return T::kSM_FatalError;
352
353 fWeather.SetVerbose(evt.GetBool());
354
355 return T::GetCurrentState();
356 }
357
358 int Disconnect()
359 {
360 // Close all connections
361 fWeather.PostClose(false);
362
363 /*
364 // Now wait until all connection have been closed and
365 // all pending handlers have been processed
366 poll();
367 */
368
369 return T::GetCurrentState();
370 }
371
372 int Reconnect(const EventImp &evt)
373 {
374 // Close all connections to supress the warning in SetEndpoint
375 fWeather.PostClose(false);
376
377 // Now wait until all connection have been closed and
378 // all pending handlers have been processed
379 poll();
380
381 if (evt.GetBool())
382 fWeather.SetEndpoint(evt.GetString());
383
384 // Now we can reopen the connection
385 fWeather.PostClose(true);
386
387 return T::GetCurrentState();
388 }
389
390 int Execute()
391 {
392 // Dispatch (execute) at most one handler from the queue. In contrary
393 // to run_one(), it doesn't wait until a handler is available
394 // which can be dispatched, so poll_one() might return with 0
395 // handlers dispatched. The handlers are always dispatched/executed
396 // synchronously, i.e. within the call to poll_one()
397 poll_one();
398
399 return fWeather.GetState();
400 }
401
402
403public:
404 StateMachineDrive(ostream &out=cout) :
405 T(out, "MAGIC_WEATHER"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
406 fWeather(*this, *this)
407 {
408 // ba::io_service::work is a kind of keep_alive for the loop.
409 // It prevents the io_service to go to stopped state, which
410 // would prevent any consecutive calls to run()
411 // or poll() to do nothing. reset() could also revoke to the
412 // previous state but this might introduce some overhead of
413 // deletion and creation of threads and more.
414
415 // State names
416 AddStateName(kStateDisconnected, "Disconnected",
417 "No connection to cosy");
418
419 AddStateName(kStateConnected, "Connected",
420 "Cosy connected, drive stopped");
421
422 AddStateName(kStateOk, "Ok",
423 "Drive system not ready for movement");
424
425 // Verbosity commands
426 T::AddEvent("SET_VERBOSE", "B")
427 (bind(&StateMachineDrive::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", kStateConnected)
433 (bind(&StateMachineDrive::Disconnect, this))
434 ("disconnect from ethernet");
435
436 AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected)
437 (bind(&StateMachineDrive::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 fWeather.StartConnect();
442 }
443
444 void SetEndpoint(const string &url)
445 {
446 fWeather.SetEndpoint(url);
447 }
448
449 int EvalOptions(Configuration &conf)
450 {
451 SetEndpoint(conf.Get<string>("addr"));
452
453 fWeather.SetVerbose(!conf.Get<bool>("quiet"));
454
455 return -1;
456 }
457};
458
459// ------------------------------------------------------------------------
460
461#include "Main.h"
462
463
464template<class T, class S, class R>
465int RunShell(Configuration &conf)
466{
467 return Main::execute<T, StateMachineDrive<S, R>>(conf);
468}
469
470void SetupConfiguration(Configuration &conf)
471{
472 po::options_description control("Drive control options");
473 control.add_options()
474 ("no-dim,d", po_switch(), "Disable dim services")
475 ("addr,a", var<string>("www.magic.iac.es:80"), "Network address of Cosy")
476 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
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 */
491void PrintUsage()
492{
493 cout <<
494 "The drivectrl is an interface to cosy.\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: drivectrl [-c type] [OPTIONS]\n"
502 " or: drivectrl [OPTIONS]\n";
503 cout << endl;
504}
505
506void 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
528int main(int argc, const char* argv[])
529{
530 Configuration conf(argv[0]);
531 conf.SetPrintUsage(PrintUsage);
532 Main::SetupConfiguration(conf);
533 SetupConfiguration(conf);
534
535 if (!conf.DoParse(argc, argv, PrintHelp))
536 return -1;
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, ConnectionWeather>(conf);
545 else
546 return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(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, ConnectionWeather>(conf);
553 else
554 return RunShell<LocalConsole, StateMachine, ConnectionWeather>(conf);
555 }
556 else
557 {
558 if (conf.Get<int>("console")==0)
559 return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
560 else
561 return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
562 }
563 }
564 /*catch (std::exception& e)
565 {
566 cerr << "Exception: " << e.what() << endl;
567 return -1;
568 }*/
569
570 return 0;
571}
Note: See TracBrowser for help on using the repository browser.