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

Last change on this file since 11812 was 11673, checked in by tbretz, 13 years ago
Added 'no-dim' option to make boost happy; added function to convert PT1000 data to temperatures (~1% rms)
File size: 17.9 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 //
82 // From: http://de.wikipedia.org/wiki/Pt100
83 //
84 double GetTempPT1000(double R) const
85 {
86 const double R0 = 1000; // 1KOhm
87
88 const double a = 3.85e-3;
89
90 return (R/R0 - 1)/a;
91 }
92
93
94 void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
95 {
96 // Do not schedule a new read if the connection failed.
97 if (bytes_received==0 || err)
98 {
99 if (err==ba::error::eof)
100 Warn("Connection closed by remote host (FTM).");
101
102 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
103 // 125: Operation canceled
104 if (err && err!=ba::error::eof && // Connection closed by remote host
105 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
106 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
107 {
108 ostringstream str;
109 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
110 Error(str);
111 }
112 PostClose(err!=ba::error::basic_errors::operation_aborted);
113 return;
114 }
115
116 if (fIsVerbose)
117 Out() << kBold << "Received (" << bytes_received << " bytes):" << endl;
118
119 /*
120 "status: 00000538 \n"
121 "time_s: 764.755 \n"
122 "VOLTAGES \n"
123 " \n"
124 "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n"
125 " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 00001111 \n"
126 "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"
127 "RESISTANCES \n"
128 " \n"
129 "enable:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n"
130 " done:11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 \n"
131 "values: \n"
132 "1000.16 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
133 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
134 "1197.07 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
135 " 558.59 677.92 817.26 989.39 1200.35 1503.06 1799.90 2204.18 \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 "3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 3199.99 \n"
140 "end.\n";
141 */
142/*
143 const unsigned int TIME_OFF = 3;
144 const unsigned int VOLT_OFF = 30;
145 const unsigned int CURR_OFF = 70;
146 const unsigned int HUMI_OFF = 110;
147 const unsigned int TEMP_OFF = 134;
148 */
149
150 if (fDump)
151 {
152 ostringstream msg;
153 msg << "--- " << Time().GetAsStr() << " --- received " << bytes_received << " bytes.";
154 Dump(msg.str());
155 }
156
157
158 istream is(&fBuffer);
159
160 int state = 0;
161 bool values = false;
162
163 vector<int> volt;
164 vector<float> resist;
165 int status=-1;
166 float time=0;
167
168 string buffer;
169 while (getline(is, buffer, '\n'))
170 {
171 if (fIsVerbose)
172 Out() << buffer << endl;
173 if (fDump)
174 Dump(buffer);
175
176 buffer = Tools::Trim(buffer);
177
178 if (buffer.empty())
179 continue;
180
181 if (buffer.substr(0, 4)=="end.")
182 break;
183
184 if (buffer.substr(0, 8)=="status: ")
185 {
186 status = stoi(buffer.substr(8));
187 continue;
188 }
189
190 if (buffer.substr(0, 8)=="time_s: ")
191 {
192 time = stof(buffer.substr(8));
193 continue;
194 }
195
196 if (buffer.substr(0, 8)=="VOLTAGES")
197 {
198 state = 1;
199 continue;
200 }
201
202 if (buffer.substr(0, 11)=="RESISTANCES")
203 {
204 state = 2;
205 continue;
206 }
207
208 if (state==1 && buffer.substr(0, 7)=="values:")
209 {
210 istringstream in(buffer.substr(7));
211 while (1)
212 {
213 int v;
214 in >> v;
215 if (!in)
216 break;
217
218 volt.push_back(v);
219 }
220 continue;
221 }
222
223 if (state==2 && buffer.substr(0, 7)=="values:")
224 {
225 values = true;
226 continue;
227 }
228
229 if (state==2 && !values)
230 continue;
231
232 istringstream in(buffer);
233 while (1)
234 {
235 float f;
236 in >> f;
237 if (!in)
238 break;
239
240 resist.push_back(f);
241 }
242 }
243
244 for (size_t i=0; i<resist.size(); i++)
245 if (resist[i]>800 && resist[i]<2000)
246 cout << setw(2) << i << " - " << setw(4) << (int)resist[i] << ": " << setprecision(1) << fixed << GetTempPT1000(resist[i]) << endl;
247 else
248 cout << setw(2) << i << " - " << setw(4) << (int)resist[i] << ": " << "----" << endl;
249
250 StartRead();
251 }
252
253 void StartRead()
254 {
255 ba::async_read_until(*this, fBuffer, "end.\n",
256 boost::bind(&ConnectionFSC::HandleReceivedData, this,
257 dummy::error, dummy::bytes_transferred, 0));
258
259 // FIXME: Add timeout here
260 }
261
262 // This is called when a connection was established
263 void ConnectionEstablished()
264 {
265 PostMessage("m", 1);
266
267 fBuffer.prepare(10000);
268 StartRead();
269 }
270
271/*
272 void HandleReadTimeout(const bs::error_code &error)
273 {
274 if (error==ba::error::basic_errors::operation_aborted)
275 return;
276
277 if (error)
278 {
279 ostringstream str;
280 str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
281 Error(str);
282
283 PostClose();
284 return;
285
286 }
287
288 if (!is_open())
289 {
290 // For example: Here we could schedule a new accept if we
291 // would not want to allow two connections at the same time.
292 return;
293 }
294
295 // Check whether the deadline has passed. We compare the deadline
296 // against the current time since a new asynchronous operation
297 // may have moved the deadline before this actor had a chance
298 // to run.
299 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
300 return;
301
302 Error("Timeout reading data from "+URL());
303
304 PostClose();
305 }
306*/
307
308public:
309 ConnectionFSC(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
310 fIsVerbose(true), fDump(false)
311 {
312 SetLogStream(&imp);
313 }
314
315 void SetVerbose(bool b)
316 {
317 fIsVerbose = b;
318 }
319
320 void SetDumpStream(bool b)
321 {
322 fDump = b;
323 }
324};
325
326// ------------------------------------------------------------------------
327
328#include "DimDescriptionService.h"
329
330class ConnectionDimFSC : public ConnectionFSC
331{
332private:
333
334 DimDescribedService fDimTemp;
335 DimDescribedService fDimHum;
336 DimDescribedService fDimVolt;
337 DimDescribedService fDimCurrent;
338
339 void Update(DimDescribedService &svc, vector<float> data, float time) const
340 {
341 data.insert(data.begin(), time);
342 svc.setData(data.data(), data.size()*sizeof(float));
343 svc.updateService();
344 }
345
346 void UpdateTemp(float time, const vector<float> &temp)
347 {
348 Update(fDimTemp, temp, time);
349 }
350
351 void UpdateHum(float time, const vector<float> &hum)
352 {
353 Update(fDimHum, hum, time);
354 }
355
356 void UpdateVolt(float time, const vector<float> &volt)
357 {
358 Update(fDimVolt, volt, time);
359 }
360
361 void UpdateCur(float time, const vector<float> &curr)
362 {
363 Update(fDimCurrent, curr, time);
364 }
365
366public:
367 ConnectionDimFSC(ba::io_service& ioservice, MessageImp &imp) :
368 ConnectionFSC(ioservice, imp),
369 fDimTemp ("FSC_CONTROL/TEMPERATURE", "F:1;F:64", ""),
370 fDimHum ("FSC_CONTROL/HUMIDITY", "F:1;F:40", ""),
371 fDimVolt ("FSC_CONTROL/VOLTAGE", "F:1;F:40", ""),
372 fDimCurrent("FSC_CONTROL/CURRENT", "F:1;F:4", "")
373 {
374 }
375
376 // 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
377};
378
379// ------------------------------------------------------------------------
380
381template <class T, class S>
382class StateMachineFSC : public T, public ba::io_service, public ba::io_service::work
383{
384 int Wrap(boost::function<void()> f)
385 {
386 f();
387 return T::GetCurrentState();
388 }
389
390 function<int(const EventImp &)> Wrapper(function<void()> func)
391 {
392 return bind(&StateMachineFSC::Wrap, this, func);
393 }
394
395private:
396 S fFSC;
397
398 enum states_t
399 {
400 kStateDisconnected = 1,
401 kStateConnected = 2,
402 };
403
404 int Disconnect()
405 {
406 // Close all connections
407 fFSC.PostClose(false);
408
409 /*
410 // Now wait until all connection have been closed and
411 // all pending handlers have been processed
412 poll();
413 */
414
415 return T::GetCurrentState();
416 }
417
418 int Reconnect(const EventImp &evt)
419 {
420 // Close all connections to supress the warning in SetEndpoint
421 fFSC.PostClose(false);
422
423 // Now wait until all connection have been closed and
424 // all pending handlers have been processed
425 poll();
426
427 if (evt.GetBool())
428 fFSC.SetEndpoint(evt.GetString());
429
430 // Now we can reopen the connection
431 fFSC.PostClose(true);
432
433 return T::GetCurrentState();
434 }
435
436 int Execute()
437 {
438 // Dispatch (execute) at most one handler from the queue. In contrary
439 // to run_one(), it doesn't wait until a handler is available
440 // which can be dispatched, so poll_one() might return with 0
441 // handlers dispatched. The handlers are always dispatched/executed
442 // synchronously, i.e. within the call to poll_one()
443 poll_one();
444
445 return fFSC.IsConnected() ? kStateConnected : kStateDisconnected;
446 }
447
448 bool CheckEventSize(size_t has, const char *name, size_t size)
449 {
450 if (has==size)
451 return true;
452
453 ostringstream msg;
454 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
455 T::Fatal(msg);
456 return false;
457 }
458
459 int SetVerbosity(const EventImp &evt)
460 {
461 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
462 return T::kSM_FatalError;
463
464 fFSC.SetVerbose(evt.GetBool());
465
466 return T::GetCurrentState();
467 }
468
469 int SetDumpStream(const EventImp &evt)
470 {
471 if (!CheckEventSize(evt.GetSize(), "SetDumpStream", 1))
472 return T::kSM_FatalError;
473
474 fFSC.SetDumpStream(evt.GetBool());
475
476 return T::GetCurrentState();
477 }
478
479public:
480 StateMachineFSC(ostream &out=cout) :
481 T(out, "FSC_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
482 fFSC(*this, *this)
483 {
484 // ba::io_service::work is a kind of keep_alive for the loop.
485 // It prevents the io_service to go to stopped state, which
486 // would prevent any consecutive calls to run()
487 // or poll() to do nothing. reset() could also revoke to the
488 // previous state but this might introduce some overhead of
489 // deletion and creation of threads and more.
490
491 // State names
492 AddStateName(kStateDisconnected, "Disconnected",
493 "FSC board not connected via ethernet.");
494
495 AddStateName(kStateConnected, "Connected",
496 "Ethernet connection to FSC established.");
497
498 // Verbosity commands
499 T::AddEvent("SET_VERBOSE", "B:1")
500 (bind(&StateMachineFSC::SetVerbosity, this, placeholders::_1))
501 ("set verbosity state"
502 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
503
504 T::AddEvent("DUMP_STREAM", "B:1")
505 (bind(&StateMachineFSC::SetDumpStream, this, placeholders::_1))
506 (""
507 "");
508
509 // Conenction commands
510 AddEvent("DISCONNECT", kStateConnected)
511 (bind(&StateMachineFSC::Disconnect, this))
512 ("disconnect from ethernet");
513
514 AddEvent("RECONNECT", "O", kStateDisconnected, kStateConnected)
515 (bind(&StateMachineFSC::Reconnect, this, placeholders::_1))
516 ("(Re)connect ethernet connection to FTM, a new address can be given"
517 "|[host][string]:new ethernet address in the form <host:port>");
518
519 fFSC.StartConnect();
520 }
521
522 void SetEndpoint(const string &url)
523 {
524 fFSC.SetEndpoint(url);
525 }
526
527 int EvalOptions(Configuration &conf)
528 {
529 SetEndpoint(conf.Get<string>("addr"));
530
531 fFSC.SetVerbose(!conf.Get<bool>("quiet"));
532
533 return -1;
534 }
535};
536
537// ------------------------------------------------------------------------
538
539#include "Main.h"
540
541template<class T, class S, class R>
542int RunShell(Configuration &conf)
543{
544 return Main::execute<T, StateMachineFSC<S, R>>(conf);
545}
546
547void SetupConfiguration(Configuration &conf)
548{
549 po::options_description control("FTM control options");
550 control.add_options()
551 ("no-dim", po_bool(), "Disable dim services")
552 ("addr,a", var<string>("localhost:5000"), "Network address of FTM")
553 ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.")
554 ;
555
556 conf.AddOptions(control);
557}
558
559/*
560 Extract usage clause(s) [if any] for SYNOPSIS.
561 Translators: "Usage" and "or" here are patterns (regular expressions) which
562 are used to match the usage synopsis in program output. An example from cp
563 (GNU coreutils) which contains both strings:
564 Usage: cp [OPTION]... [-T] SOURCE DEST
565 or: cp [OPTION]... SOURCE... DIRECTORY
566 or: cp [OPTION]... -t DIRECTORY SOURCE...
567 */
568void PrintUsage()
569{
570 cout <<
571 "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
572 "\n"
573 "The default is that the program is started without user intercation. "
574 "All actions are supposed to arrive as DimCommands. Using the -c "
575 "option, a local shell can be initialized. With h or help a short "
576 "help message about the usuage can be brought to the screen.\n"
577 "\n"
578 "Usage: fscctrl [-c type] [OPTIONS]\n"
579 " or: fscctrl [OPTIONS]\n";
580 cout << endl;
581}
582
583void PrintHelp()
584{
585 /* Additional help text which is printed after the configuration
586 options goes here */
587
588 /*
589 cout << "bla bla bla" << endl << endl;
590 cout << endl;
591 cout << "Environment:" << endl;
592 cout << "environment" << endl;
593 cout << endl;
594 cout << "Examples:" << endl;
595 cout << "test exam" << endl;
596 cout << endl;
597 cout << "Files:" << endl;
598 cout << "files" << endl;
599 cout << endl;
600 */
601}
602
603int main(int argc, const char* argv[])
604{
605 Configuration conf(argv[0]);
606 conf.SetPrintUsage(PrintUsage);
607 Main::SetupConfiguration(conf);
608 SetupConfiguration(conf);
609
610 if (!conf.DoParse(argc, argv, PrintHelp))
611 return -1;
612
613 //try
614 {
615 // No console access at all
616 if (!conf.Has("console"))
617 {
618 if (conf.Get<bool>("no-dim"))
619 return RunShell<LocalStream, StateMachine, ConnectionFSC>(conf);
620 else
621 return RunShell<LocalStream, StateMachineDim, ConnectionDimFSC>(conf);
622 }
623 // Cosole access w/ and w/o Dim
624 if (conf.Get<bool>("no-dim"))
625 {
626 if (conf.Get<int>("console")==0)
627 return RunShell<LocalShell, StateMachine, ConnectionFSC>(conf);
628 else
629 return RunShell<LocalConsole, StateMachine, ConnectionFSC>(conf);
630 }
631 else
632 {
633 if (conf.Get<int>("console")==0)
634 return RunShell<LocalShell, StateMachineDim, ConnectionDimFSC>(conf);
635 else
636 return RunShell<LocalConsole, StateMachineDim, ConnectionDimFSC>(conf);
637 }
638 }
639 /*catch (std::exception& e)
640 {
641 cerr << "Exception: " << e.what() << endl;
642 return -1;
643 }*/
644
645 return 0;
646}
Note: See TracBrowser for help on using the repository browser.