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

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