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

Last change on this file since 11581 was 11578, checked in by tbretz, 13 years ago
Moved some part of the initialization from main to Configure::DoParse
File size: 17.3 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
522template<class T, class S, class R>
523int RunShell(Configuration &conf)
524{
525 return Main::execute<T, StateMachineFSC<S, R>>(conf);
526}
527
528void SetupConfiguration(Configuration &conf)
529{
530 po::options_description control("FTM control options");
531 control.add_options()
532 ("addr,a", var<string>("localhost:5000"), "Network address of FTM")
533 ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.")
534 ;
535
536 conf.AddOptions(control);
537}
538
539/*
540 Extract usage clause(s) [if any] for SYNOPSIS.
541 Translators: "Usage" and "or" here are patterns (regular expressions) which
542 are used to match the usage synopsis in program output. An example from cp
543 (GNU coreutils) which contains both strings:
544 Usage: cp [OPTION]... [-T] SOURCE DEST
545 or: cp [OPTION]... SOURCE... DIRECTORY
546 or: cp [OPTION]... -t DIRECTORY SOURCE...
547 */
548void PrintUsage()
549{
550 cout <<
551 "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
552 "\n"
553 "The default is that the program is started without user intercation. "
554 "All actions are supposed to arrive as DimCommands. Using the -c "
555 "option, a local shell can be initialized. With h or help a short "
556 "help message about the usuage can be brought to the screen.\n"
557 "\n"
558 "Usage: fscctrl [-c type] [OPTIONS]\n"
559 " or: fscctrl [OPTIONS]\n";
560 cout << endl;
561}
562
563void PrintHelp()
564{
565 /* Additional help text which is printed after the configuration
566 options goes here */
567
568 /*
569 cout << "bla bla bla" << endl << endl;
570 cout << endl;
571 cout << "Environment:" << endl;
572 cout << "environment" << endl;
573 cout << endl;
574 cout << "Examples:" << endl;
575 cout << "test exam" << endl;
576 cout << endl;
577 cout << "Files:" << endl;
578 cout << "files" << endl;
579 cout << endl;
580 */
581}
582
583int main(int argc, const char* argv[])
584{
585 Configuration conf(argv[0]);
586 conf.SetPrintUsage(PrintUsage);
587 Main::SetupConfiguration(conf);
588 SetupConfiguration(conf);
589
590 if (!conf.DoParse(argc, argv, PrintHelp))
591 return -1;
592
593 //try
594 {
595 // No console access at all
596 if (!conf.Has("console"))
597 {
598 if (conf.Get<bool>("no-dim"))
599 return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
600 else
601 return RunShell<LocalStream, StateMachineDim, ConnectionDimFSC>(conf);
602 }
603 // Cosole access w/ and w/o Dim
604 if (conf.Get<bool>("no-dim"))
605 {
606 if (conf.Get<int>("console")==0)
607 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
608 else
609 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
610 }
611 else
612 {
613 if (conf.Get<int>("console")==0)
614 return RunShell<LocalShell, StateMachineDim, ConnectionDimFSC>(conf);
615 else
616 return RunShell<LocalConsole, StateMachineDim, ConnectionDimFSC>(conf);
617 }
618 }
619 /*catch (std::exception& e)
620 {
621 cerr << "Exception: " << e.what() << endl;
622 return -1;
623 }*/
624
625 return 0;
626}
Note: See TracBrowser for help on using the repository browser.