source: trunk/FACT++/src/fscctrl.cc@ 11103

Last change on this file since 11103 was 11076, checked in by tbretz, 13 years ago
Updated FTM to FSC
File size: 19.5 KB
Line 
1#include <boost/bind.hpp>
2#include <boost/array.hpp>
3#if BOOST_VERSION < 104400
4#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
5#undef BOOST_HAS_RVALUE_REFS
6#endif
7#endif
8#include <boost/thread.hpp>
9#include <boost/asio/error.hpp>
10#include <boost/asio/deadline_timer.hpp>
11
12#include "Dim.h"
13#include "Event.h"
14#include "Shell.h"
15#include "StateMachineDim.h"
16#include "Connection.h"
17#include "Configuration.h"
18#include "Console.h"
19#include "Converter.h"
20
21#include "tools.h"
22
23#include "LocalControl.h"
24
25
26namespace ba = boost::asio;
27namespace bs = boost::system;
28namespace dummy = ba::placeholders;
29
30using namespace std;
31
32// ------------------------------------------------------------------------
33
34class ConnectionFSC : public Connection
35{
36 boost::asio::streambuf fBuffer;
37
38 bool fIsVerbose;
39
40protected:
41
42 virtual void UpdateTemp(float, const vector<float> &)
43 {
44 }
45
46 virtual void UpdateHum(float, const vector<float>&)
47 {
48 }
49
50 virtual void UpdateVolt(float, const vector<float>&)
51 {
52 }
53
54 virtual void UpdateCur(float, const vector<float>&)
55 {
56 }
57
58 /*
59 virtual void UpdateError()
60 {
61 if (!fIsVerbose)
62 return;
63
64 Out() << endl << kRed << "Error received:" << endl;
65 Out() << fError;
66 if (fIsHexOutput)
67 Out() << Converter::GetHex<uint16_t>(fError, 16) << endl;
68 }
69*/
70
71private:
72 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
73 {
74 // Do not schedule a new read if the connection failed.
75 if (bytes_received==0 || err)
76 {
77 if (err==ba::error::eof)
78 Warn("Connection closed by remote host (FTM).");
79
80 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
81 // 125: Operation canceled
82 if (err && err!=ba::error::eof && // Connection closed by remote host
83 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
84 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
85 {
86 ostringstream str;
87 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
88 Error(str);
89 }
90 PostClose(err!=ba::error::basic_errors::operation_aborted);
91 return;
92 }
93
94 if (fIsVerbose)
95 Out() << kBold << "Received:" << endl;
96
97 /*
98 "status: 00000538 \n"
99 "time_s: 764.755 \n"
100 "VOLTAGES \n"
101 " \n"
102 "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n"
103 " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n"
104 "values:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 \n"
105 "RESISTANCES \n"
106 " \n"
107 "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n"
108 " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n"
109 "values: \n"
110 "1000.16 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
111 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
112 "1197.07 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
113 " 558.59 677.92 817.26 989.39 1200.35 1503.06 1799.90 2204.18 \n"
114 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
115 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
116 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
117 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
118 "end.\n";
119 */
120/*
121 const unsigned int TIME_OFF = 3;
122 const unsigned int VOLT_OFF = 30;
123 const unsigned int CURR_OFF = 70;
124 const unsigned int HUMI_OFF = 110;
125 const unsigned int TEMP_OFF = 134;
126*/
127 istream is(&fBuffer);
128
129 int state = 0;
130 bool values = false;
131
132 vector<int> volt;
133 vector<float> resist;
134 int status=-1;
135 float time=0;
136
137 string buffer;
138 while (getline(is, buffer, '\n'))
139 {
140 if (fIsVerbose)
141 Out() << buffer << endl;
142
143 buffer = Tools::Trim(buffer);
144
145 if (buffer.empty())
146 continue;
147
148 if (buffer.substr(0, 4)=="end.")
149 break;
150
151 if (buffer.substr(0, 8)=="status: ")
152 {
153 status = atoi(buffer.c_str()+8);
154 continue;
155 }
156
157 if (buffer.substr(0, 8)=="time_s: ")
158 {
159 time = atof(buffer.c_str()+8);
160 continue;
161 }
162
163 if (buffer.substr(0, 8)=="VOLTAGES")
164 {
165 state = 1;
166 continue;
167 }
168
169 if (buffer.substr(0, 11)=="RESISTANCES")
170 {
171 state = 2;
172 continue;
173 }
174
175 if (state==1 && buffer.substr(0, 7)=="values:")
176 {
177 istringstream in(buffer.substr(7));
178 while (1)
179 {
180 int v;
181 in >> v;
182 if (!in)
183 break;
184
185 volt.push_back(v);
186 }
187 continue;
188 }
189
190 if (state==2 && buffer.substr(0, 7)=="values:")
191 {
192 values = true;
193 continue;
194 }
195
196 if (state==2 && !values)
197 continue;
198
199 istringstream in(buffer);
200 while (1)
201 {
202 float f;
203 in >> f;
204 if (!in)
205 break;
206
207 resist.push_back(f);
208 }
209 }
210
211 StartRead();
212 }
213
214 void StartRead()
215 {
216 ba::async_read_until(*this, fBuffer, "end.\n",
217 boost::bind(&ConnectionFSC::HandleReceivedData, this,
218 dummy::error, dummy::bytes_transferred, 0));
219
220 // FIXME: Add timeout here
221 }
222
223 // This is called when a connection was established
224 void ConnectionEstablished()
225 {
226 PostMessage("m", 1);
227
228 fBuffer.prepare(10000);
229 StartRead();
230 }
231
232/*
233 void HandleReadTimeout(const bs::error_code &error)
234 {
235 if (error==ba::error::basic_errors::operation_aborted)
236 return;
237
238 if (error)
239 {
240 ostringstream str;
241 str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
242 Error(str);
243
244 PostClose();
245 return;
246
247 }
248
249 if (!is_open())
250 {
251 // For example: Here we could schedule a new accept if we
252 // would not want to allow two connections at the same time.
253 return;
254 }
255
256 // Check whether the deadline has passed. We compare the deadline
257 // against the current time since a new asynchronous operation
258 // may have moved the deadline before this actor had a chance
259 // to run.
260 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
261 return;
262
263 Error("Timeout reading data from "+URL());
264
265 PostClose();
266 }
267*/
268
269public:
270 ConnectionFSC(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
271 fIsVerbose(true)
272 {
273 SetLogStream(&imp);
274 }
275
276 void SetVerbose(bool b)
277 {
278 fIsVerbose = b;
279 }
280};
281
282// ------------------------------------------------------------------------
283
284#include "DimDescriptionService.h"
285
286class ConnectionDimFSC : public ConnectionFSC
287{
288private:
289
290 DimDescribedService fDimTemp;
291 DimDescribedService fDimHum;
292 DimDescribedService fDimVolt;
293 DimDescribedService fDimCurrent;
294
295 void Update(DimDescribedService &svc, vector<float> data, float time) const
296 {
297 data.insert(data.begin(), time);
298 svc.setData(data.data(), data.size()*sizeof(float));
299 svc.updateService();
300 }
301
302 void UpdateTemp(float time, const vector<float> &temp)
303 {
304 Update(fDimTemp, temp, time);
305 }
306
307 void UpdateHum(float time, const vector<float> &hum)
308 {
309 Update(fDimHum, hum, time);
310 }
311
312 void UpdateVolt(float time, const vector<float> &volt)
313 {
314 Update(fDimVolt, volt, time);
315 }
316
317 void UpdateCur(float time, const vector<float> &curr)
318 {
319 Update(fDimCurrent, curr, time);
320 }
321
322public:
323 ConnectionDimFSC(ba::io_service& ioservice, MessageImp &imp) :
324 ConnectionFSC(ioservice, imp),
325 fDimTemp ("FSC_CONTROL/TEMPERATURE", "F:1;F:64", ""),
326 fDimHum ("FSC_CONTROL/HUMIDITY", "F:1;F:40", ""),
327 fDimVolt ("FSC_CONTROL/VOLTAGE", "F:1;F:40", ""),
328 fDimCurrent("FSC_CONTROL/CURRENT", "F:1;F:4", "")
329 {
330 }
331
332 // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z
333};
334
335// ------------------------------------------------------------------------
336
337template <class T, class S>
338class StateMachineFSC : public T, public ba::io_service, public ba::io_service::work
339{
340 int Wrap(boost::function<void()> f)
341 {
342 f();
343 return T::GetCurrentState();
344 }
345
346 boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
347 {
348 return boost::bind(&StateMachineFSC::Wrap, this, func);
349 }
350
351private:
352 S fFSC;
353
354 enum states_t
355 {
356 kStateDisconnected = 1,
357 kStateConnected = 2,
358 };
359
360 int Disconnect()
361 {
362 // Close all connections
363 fFSC.PostClose(false);
364
365 /*
366 // Now wait until all connection have been closed and
367 // all pending handlers have been processed
368 poll();
369 */
370
371 return T::GetCurrentState();
372 }
373
374 int Reconnect(const EventImp &evt)
375 {
376 // Close all connections to supress the warning in SetEndpoint
377 fFSC.PostClose(false);
378
379 // Now wait until all connection have been closed and
380 // all pending handlers have been processed
381 poll();
382
383 if (evt.GetBool())
384 fFSC.SetEndpoint(evt.GetString());
385
386 // Now we can reopen the connection
387 fFSC.PostClose(true);
388
389 return T::GetCurrentState();
390 }
391
392 int Execute()
393 {
394 // Dispatch (execute) at most one handler from the queue. In contrary
395 // to run_one(), it doesn't wait until a handler is available
396 // which can be dispatched, so poll_one() might return with 0
397 // handlers dispatched. The handlers are always dispatched/executed
398 // synchronously, i.e. within the call to poll_one()
399 poll_one();
400
401 return fFSC.IsConnected() ? kStateConnected : kStateDisconnected;
402 }
403
404 bool CheckEventSize(size_t has, const char *name, size_t size)
405 {
406 if (has==size)
407 return true;
408
409 ostringstream msg;
410 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
411 T::Fatal(msg);
412 return false;
413 }
414
415 int SetVerbosity(const EventImp &evt)
416 {
417 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
418 return T::kSM_FatalError;
419
420 fFSC.SetVerbose(evt.GetBool());
421
422 return T::GetCurrentState();
423 }
424
425public:
426 StateMachineFSC(ostream &out=cout) :
427 T(out, "FSC_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
428 fFSC(*this, *this)
429 {
430 // ba::io_service::work is a kind of keep_alive for the loop.
431 // It prevents the io_service to go to stopped state, which
432 // would prevent any consecutive calls to run()
433 // or poll() to do nothing. reset() could also revoke to the
434 // previous state but this might introduce some overhead of
435 // deletion and creation of threads and more.
436
437 // State names
438 AddStateName(kStateDisconnected, "Disconnected",
439 "FSC board not connected via ethernet.");
440
441 AddStateName(kStateConnected, "Connected",
442 "Ethernet connection to FSC established.");
443
444 // Verbosity commands
445 T::AddEvent("SET_VERBOSE", "B")
446 (boost::bind(&StateMachineFSC::SetVerbosity, this, _1))
447 ("set verbosity state"
448 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
449
450 // Conenction commands
451 AddEvent("DISCONNECT", kStateConnected)
452 (boost::bind(&StateMachineFSC::Disconnect, this))
453 ("disconnect from ethernet");
454
455 AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected)
456 (boost::bind(&StateMachineFSC::Reconnect, this, _1))
457 ("(Re)connect ethernet connection to FTM, a new address can be given"
458 "|[host][string]:new ethernet address in the form <host:port>");
459
460 fFSC.StartConnect();
461 }
462
463 void SetEndpoint(const string &url)
464 {
465 fFSC.SetEndpoint(url);
466 }
467
468 bool SetConfiguration(const Configuration &conf)
469 {
470 SetEndpoint(conf.Get<string>("addr"));
471
472 fFSC.SetVerbose(!conf.Get<bool>("quiet"));
473
474 return true;
475 }
476};
477
478// ------------------------------------------------------------------------
479
480void RunThread(StateMachineImp *io_service)
481{
482 // This is necessary so that the StateMachien Thread can signal the
483 // Readline to exit
484 io_service->Run();
485 Readline::Stop();
486}
487
488/*
489template<class S, class T>
490int RunDim(Configuration &conf)
491{
492 WindowLog wout;
493
494 ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);
495
496
497 if (conf.Has("log"))
498 if (!wout.OpenLogFile(conf.Get<string>("log")))
499 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
500
501 // Start io_service.Run to use the StateMachineImp::Run() loop
502 // Start io_service.run to only use the commandHandler command detaching
503 StateMachineFSC<S, T> io_service(wout);
504 if (!io_service.SetConfiguration(conf))
505 return -1;
506
507 io_service.Run();
508
509 return 0;
510}
511*/
512
513template<class T, class S, class R>
514int RunShell(Configuration &conf)
515{
516 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
517
518 WindowLog &win = shell.GetStreamIn();
519 WindowLog &wout = shell.GetStreamOut();
520
521 if (conf.Has("log"))
522 if (!wout.OpenLogFile(conf.Get<string>("log")))
523 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
524
525 StateMachineFSC<S, R> io_service(wout);
526 if (!io_service.SetConfiguration(conf))
527 return -1;
528
529 shell.SetReceiver(io_service);
530
531 boost::thread t(boost::bind(RunThread, &io_service));
532 // boost::thread t(boost::bind(&StateMachineFSC<S>::Run, &io_service));
533
534 if (conf.Has("exec"))
535 {
536 const vector<string> v = conf.Get<vector<string>>("exec");
537 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
538 shell.Execute(*it);
539 }
540
541 shell.Run(); // Run the shell
542 io_service.Stop(); // Signal Loop-thread to stop
543 // io_service.Close(); // Obsolete, done by the destructor
544
545 // Wait until the StateMachine has finished its thread
546 // before returning and destroying the dim objects which might
547 // still be in use.
548 t.join();
549
550 return 0;
551}
552
553void SetupConfiguration(Configuration &conf)
554{
555 const string n = conf.GetName()+".log";
556
557 po::options_description config("Program options");
558 config.add_options()
559 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
560 ("log,l", var<string>(n), "Write log-file")
561 ("no-dim,d", po_bool(), "Disable dim services")
562 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
563 ("exec,e", vars<string>(), "Execute one or more scrips at startup")
564 ;
565
566 po::options_description control("FTM control options");
567 control.add_options()
568 ("addr,a", var<string>("localhost:5000"), "Network address of FTM")
569 ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.")
570 ;
571
572 conf.AddEnv("dns", "DIM_DNS_NODE");
573
574 conf.AddOptions(config);
575 conf.AddOptions(control);
576}
577
578/*
579 Extract usage clause(s) [if any] for SYNOPSIS.
580 Translators: "Usage" and "or" here are patterns (regular expressions) which
581 are used to match the usage synopsis in program output. An example from cp
582 (GNU coreutils) which contains both strings:
583 Usage: cp [OPTION]... [-T] SOURCE DEST
584 or: cp [OPTION]... SOURCE... DIRECTORY
585 or: cp [OPTION]... -t DIRECTORY SOURCE...
586 */
587void PrintUsage()
588{
589 cout <<
590 "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
591 "\n"
592 "The default is that the program is started without user intercation. "
593 "All actions are supposed to arrive as DimCommands. Using the -c "
594 "option, a local shell can be initialized. With h or help a short "
595 "help message about the usuage can be brought to the screen.\n"
596 "\n"
597 "Usage: fscctrl [-c type] [OPTIONS]\n"
598 " or: fscctrl [OPTIONS]\n";
599 cout << endl;
600}
601
602void PrintHelp()
603{
604 /* Additional help text which is printed after the configuration
605 options goes here */
606
607 /*
608 cout << "bla bla bla" << endl << endl;
609 cout << endl;
610 cout << "Environment:" << endl;
611 cout << "environment" << endl;
612 cout << endl;
613 cout << "Examples:" << endl;
614 cout << "test exam" << endl;
615 cout << endl;
616 cout << "Files:" << endl;
617 cout << "files" << endl;
618 cout << endl;
619 */
620}
621
622int main(int argc, const char* argv[])
623{
624 Configuration conf(argv[0]);
625 conf.SetPrintUsage(PrintUsage);
626 SetupConfiguration(conf);
627
628 po::variables_map vm;
629 try
630 {
631 vm = conf.Parse(argc, argv);
632 }
633#if BOOST_VERSION > 104000
634 catch (po::multiple_occurrences &e)
635 {
636 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
637 return -1;
638 }
639#endif
640 catch (exception& e)
641 {
642 cerr << "Program options invalid due to: " << e.what() << endl;
643 return -1;
644 }
645
646 if (conf.HasVersion() || conf.HasPrint())
647 return -1;
648
649 if (conf.HasHelp())
650 {
651 PrintHelp();
652 return -1;
653 }
654
655 Dim::Setup(conf.Get<string>("dns"));
656
657 //try
658 {
659 // No console access at all
660 if (!conf.Has("console"))
661 {
662 if (conf.Get<bool>("no-dim"))
663 return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
664 else
665 return RunShell<LocalStream, StateMachineDim, ConnectionDimFSC>(conf);
666 }
667 // Cosole access w/ and w/o Dim
668 if (conf.Get<bool>("no-dim"))
669 {
670 if (conf.Get<int>("console")==0)
671 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
672 else
673 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
674 }
675 else
676 {
677 if (conf.Get<int>("console")==0)
678 return RunShell<LocalShell, StateMachineDim, ConnectionDimFSC>(conf);
679 else
680 return RunShell<LocalConsole, StateMachineDim, ConnectionDimFSC>(conf);
681 }
682 }
683 /*catch (std::exception& e)
684 {
685 cerr << "Exception: " << e.what() << endl;
686 return -1;
687 }*/
688
689 return 0;
690}
Note: See TracBrowser for help on using the repository browser.