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

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