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

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