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

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