source: trunk/FACT++/src/agilentctrl.cc@ 14317

Last change on this file since 14317 was 14317, checked in by neise, 12 years ago
added RequestStatus, after Output on/off; output on/off was only allowed in Connected, but not in Voltage_On/_Off corrected that
File size: 16.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 "LocalControl.h"
9#include "Configuration.h"
10#include "Console.h"
11#include "Converter.h"
12
13#include "tools.h"
14
15#include "HeadersAgilent.h"
16
17namespace ba = boost::asio;
18namespace bs = boost::system;
19namespace bapla = ba::placeholders;
20
21using namespace std;
22using namespace Agilent;
23
24// ------------------------------------------------------------------------
25
26class ConnectionAgilent : public Connection
27{
28 boost::asio::streambuf fBuffer;
29 bool fIsVerbose;
30 bool fDump;
31 ofstream fDumpStream;
32 int fState;
33 float fMeasuredVoltage;
34 float fMeasuredCurrent;
35
36
37protected:
38
39 virtual void UpdateDim(const vector<float> &)
40 {
41 }
42
43 void Dump(const string &str)
44 {
45 if (!fDumpStream.is_open())
46 {
47 fDumpStream.open("socket_dump-agilent.txt", ios::app);
48 if (!fDumpStream)
49 {
50 //ostringstream str;
51 //str << "Open file " << name << ": " << strerror(errno) << " (errno=" << errno << ")";
52 //Error(str);
53
54 return;
55 }
56 }
57
58 fDumpStream << str << endl;
59 }
60
61 boost::asio::deadline_timer fCheckStatusTimer;
62
63 void PostStatusRequest()
64 {
65 PostMessage(string("*IDN?\n"));
66 PostMessage(string("meas:volt?\n"));
67 PostMessage(string("meas:curr?\n"));
68 }
69
70
71 void RequestStatus()
72 {
73 PostStatusRequest();
74
75 fCheckStatusTimer.expires_from_now(boost::posix_time::seconds(60));
76 fCheckStatusTimer.async_wait(boost::bind(&ConnectionAgilent::HandleRequest,
77 this, bapla::error));
78 }
79
80 void HandleRequest(const bs::error_code &error)
81 {
82 // 125: Operation canceled (bs::error_code(125, bs::system_category))
83 if (error && error!=ba::error::basic_errors::operation_aborted)
84 {
85 ostringstream str;
86 str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
87 Error(str);
88
89 PostClose(false);
90 return;
91 }
92
93 if (!is_open())
94 {
95 // For example: Here we could schedule a new accept if we
96 // would not want to allow two connections at the same time.
97 PostClose(true);
98 return;
99 }
100
101 // Check whether the deadline has passed. We compare the deadline
102 // against the current time since a new asynchronous operation
103 // may have moved the deadline before this actor had a chance
104 // to run.
105 if (fCheckStatusTimer.expires_at() > ba::deadline_timer::traits_type::now())
106 return;
107
108 RequestStatus();
109 }
110private:
111
112 int fLineCounter;
113
114 void ReceivedStatusHandler(const bs::error_code& err, size_t bytes_received, int /*type*/)
115 {
116
117 // Do not schedule a new read if the connection failed.
118 if (bytes_received==0 || err)
119 {
120 if (err==ba::error::eof)
121 Warn("Connection closed by remote host (FTM).");
122
123 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
124 // 125: Operation canceled
125 if (err && err!=ba::error::eof && // Connection closed by remote host
126 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
127 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
128 {
129 ostringstream str;
130 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
131 Error(str);
132 }
133 PostClose(err!=ba::error::basic_errors::operation_aborted);
134 return;
135 }
136
137 if (fIsVerbose)
138 Out() << kBold << "Received (" << bytes_received << " bytes):" << endl;
139 /*
140 if (fDump)
141 {
142 ostringstream msg;
143 msg << "--- " << Time().GetAsStr() << " --- received " << bytes_received << " bytes.";
144 Dump(msg.str());
145 }
146*/
147 istream is(&fBuffer);
148 // data should contain two floats:
149 // * measured output voltage in volts
150 // * measured ouput current in amperes
151 vector<float> data;
152
153 fLineCounter++;
154
155 if (fLineCounter == 1)
156 {
157 // this is the Agilent identity string, do nothing
158 string s;
159 is >> s;
160 Out() << "ID string: " << s << endl;
161 }
162 else if (fLineCounter == 2)
163 {
164 // this should be a float containing the measured voltage
165 is >> fMeasuredVoltage;
166 data.push_back(fMeasuredVoltage);
167 Out() << "voltage: " << fMeasuredVoltage << endl;
168 }
169 else if (fLineCounter >= 3)
170 {
171 // this should be a float containing the measured voltage
172 is >> fMeasuredCurrent;
173 data.push_back(fMeasuredCurrent);
174 Out() << "current: " << fMeasuredCurrent << endl;
175 fLineCounter = 0;
176 }
177
178 // read the buffer empty ...
179 // FIXME: I surely misunderstand that damn fBuffer thing.
180 // I thought it should be emtpy by now...
181 string buffer;
182 while (getline(is,buffer, '\n'))
183 {
184 buffer = Tools::Trim(buffer);
185 if (buffer.empty()) continue;
186 }
187
188
189 if (fMeasuredVoltage > 1.0)
190 {
191 fState = State::kVoltage_On;
192 }
193 else
194 {
195 fState = State::kVoltage_Off;
196 }
197
198 UpdateDim(data);
199
200 Start_async_read_until();
201 }
202
203 void Start_async_read_until()
204 {
205 // Bind Received Data Handler to the event: "received <enter> character"
206 // this Handler will try to parse the incoming data
207 ba::async_read_until(*this, fBuffer, "\n",
208 boost::bind(&ConnectionAgilent::ReceivedStatusHandler, this,
209 bapla::error, bapla::bytes_transferred, 0));
210
211 // FIXME: Add timeout here
212 }
213
214 // This is called when a connection was established
215 void ConnectionEstablished()
216 {
217 fState = State::kConnected;
218 Start_async_read_until();
219 RequestStatus();
220
221 fLineCounter = 0;
222 fBuffer.prepare(1000);
223 }
224
225public:
226
227 ConnectionAgilent(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
228 fIsVerbose(true),
229 fDump(true),
230 fCheckStatusTimer(ioservice)
231 {
232 SetLogStream(&imp);
233 fState = State::kDisconnected;
234 fMeasuredVoltage=-1;
235 fMeasuredCurrent=-1;
236 }
237
238 void SetVerbose(bool b)
239 {
240 fIsVerbose = b;
241 }
242
243 void SetDumpStream(bool b)
244 {
245 fDump = b;
246 }
247
248 void SetOutput(bool b)
249 {
250 if (b)
251 {
252 PostMessage(string("outp on\n"));
253 }
254 else
255 {
256 PostMessage(string("outp off\n"));
257 }
258 RequestStatus();
259 }
260
261 int GetState() const
262 {
263 return fState;
264 }
265
266
267};
268
269// ------------------------------------------------------------------------
270
271#include "DimDescriptionService.h"
272
273class ConnectionDimAgilent : public ConnectionAgilent
274{
275private:
276
277 DimDescribedService fDim;
278
279 void Update(DimDescribedService &svc, vector<float> data) const
280 {
281 svc.Update(data);
282 }
283
284 void UpdateDim(const vector<float> &data)
285 {
286 Update(fDim, data);
287 }
288
289
290public:
291 ConnectionDimAgilent(ba::io_service& ioservice, MessageImp &imp) :
292 ConnectionAgilent(ioservice, imp),
293 fDim("AGILENT_CONTROL/DATA", "F:1;F:1",
294 "|U[V]: output voltage"
295 "|I[A]: output current")
296 {
297 // nothing happens here.
298 }
299};
300
301// ------------------------------------------------------------------------
302
303template <class T, class S>
304class StateMachineAgilent : public T, public ba::io_service, public ba::io_service::work
305{
306private:
307 S fAgilent;
308
309 int Disconnect()
310 {
311 // Close all connections
312 fAgilent.PostClose(false);
313
314 /*
315 // Now wait until all connection have been closed and
316 // all pending handlers have been processed
317 poll();
318 */
319
320 return T::GetCurrentState();
321 }
322
323 int Reconnect(const EventImp &evt)
324 {
325 // Close all connections to supress the warning in SetEndpoint
326 fAgilent.PostClose(false);
327
328 // Now wait until all connection have been closed and
329 // all pending handlers have been processed
330 poll();
331
332 if (evt.GetBool())
333 fAgilent.SetEndpoint(evt.GetString());
334
335 // Now we can reopen the connection
336 fAgilent.PostClose(true);
337
338 return T::GetCurrentState();
339 }
340
341 int Execute()
342 {
343 // Dispatch (execute) at most one handler from the queue. In contrary
344 // to run_one(), it doesn't wait until a handler is available
345 // which can be dispatched, so poll_one() might return with 0
346 // handlers dispatched. The handlers are always dispatched/executed
347 // synchronously, i.e. within the call to poll_one()
348 poll_one();
349
350 if ( fAgilent.IsConnected() )
351 return fAgilent.GetState();
352 else
353 return State::kDisconnected;
354 }
355
356 bool CheckEventSize(size_t has, const char *name, size_t size)
357 {
358 if (has==size)
359 return true;
360
361 ostringstream msg;
362 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
363 T::Fatal(msg);
364 return false;
365 }
366
367 int SetVerbosity(const EventImp &evt)
368 {
369 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
370 return T::kSM_FatalError;
371
372 fAgilent.SetVerbose(evt.GetBool());
373
374 return T::GetCurrentState();
375 }
376
377 int SetDumpStream(const EventImp &evt)
378 {
379 if (!CheckEventSize(evt.GetSize(), "SetDumpStream", 1))
380 return T::kSM_FatalError;
381
382 fAgilent.SetDumpStream(evt.GetBool());
383
384 return T::GetCurrentState();
385 }
386
387 int SetOutput(const EventImp &evt)
388 {
389 if (!CheckEventSize(evt.GetSize(), "SetOutput", 1))
390 return T::kSM_FatalError;
391
392 fAgilent.SetOutput(evt.GetBool());
393
394 return T::GetCurrentState();
395 }
396
397
398public:
399 StateMachineAgilent(ostream &out=cout) :
400 T(out, "AGILENT_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
401 fAgilent(*this, *this)
402 {
403 // ba::io_service::work is a kind of keep_alive for the loop.
404 // It prevents the io_service to go to stopped state, which
405 // would prevent any consecutive calls to run()
406 // or poll() to do nothing. reset() could also revoke to the
407 // previous state but this might introduce some overhead of
408 // deletion and creation of threads and more.
409
410 // State names
411 T::AddStateName(State::kDisconnected, "Disconnected",
412 "Agilent not connected via ethernet.");
413
414 T::AddStateName(State::kConnected, "Connected",
415 "Ethernet connection to Agilent established.");
416
417 T::AddStateName(State::kVoltage_On, "Voltage_On",
418 "The measured output voltage is higher than 1.0V");
419
420 T::AddStateName(State::kVoltage_Off, "Voltage_Off",
421 "The measured output voltage is lower than 1.0V");
422
423 // Verbosity commands
424 T::AddEvent("SET_VERBOSE", "B:1")
425 (bind(&StateMachineAgilent::SetVerbosity, this, placeholders::_1))
426 ("set verbosity state"
427 "|verbosity[bool]:disable or enable verbosity for received data (yes/no)");
428
429 T::AddEvent("DUMP_STREAM", "B:1")
430 (bind(&StateMachineAgilent::SetDumpStream, this, placeholders::_1))
431 (""
432 "");
433
434 // Conenction commands
435 T::AddEvent("DISCONNECT", State::kConnected)
436 (bind(&StateMachineAgilent::Disconnect, this))
437 ("disconnect from ethernet");
438
439 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
440 (bind(&StateMachineAgilent::Reconnect, this, placeholders::_1))
441 ("(Re)connect ethernet connection to Agilent, a new address can be given"
442 "|[host][string]:new ethernet address in the form <host:port>");
443
444 T::AddEvent("OUTPUT", "B:1")
445 (bind(&StateMachineAgilent::SetOutput, this, placeholders::_1))
446 ("set output on or off"
447 "|[state][boolean]: output setting (1;0 or 'on';'off')");
448
449 fAgilent.StartConnect();
450 }
451
452 void SetEndpoint(const string &url)
453 {
454 fAgilent.SetEndpoint(url);
455 }
456
457 int EvalOptions(Configuration &conf)
458 {
459 fAgilent.SetVerbose(!conf.Get<bool>("quiet"));
460
461 SetEndpoint(conf.Get<string>("addr"));
462
463 return -1;
464 }
465};
466
467// ------------------------------------------------------------------------
468
469#include "Main.h"
470
471template<class T, class S, class R>
472int RunShell(Configuration &conf)
473{
474 return Main::execute<T, StateMachineAgilent<S, R>>(conf);
475}
476
477void SetupConfiguration(Configuration &conf)
478{
479 po::options_description control("agilent_ctrl control options");
480 control.add_options()
481 ("no-dim", po_bool(), "Disable dim services")
482// ("addr,a", var<string>("localhost:8080"), "network address of Agilent")
483 ("addr,a", var<string>("10.0.100.220:5025"), "network address of Agilent")
484 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
485 ;
486
487 conf.AddOptions(control);
488}
489
490/*
491 Extract usage clause(s) [if any] for SYNOPSIS.
492 Translators: "Usage" and "or" here are patterns (regular expressions) which
493 are used to match the usage synopsis in program output. An example from cp
494 (GNU coreutils) which contains both strings:
495 Usage: cp [OPTION]... [-T] SOURCE DEST
496 or: cp [OPTION]... SOURCE... DIRECTORY
497 or: cp [OPTION]... -t DIRECTORY SOURCE...
498 */
499void PrintUsage()
500{
501 cout <<
502 "The ftmctrl controls the FSC (FACT Slow Control) board.\n"
503 "\n"
504 "The default is that the program is started without user intercation. "
505 "All actions are supposed to arrive as DimCommands. Using the -c "
506 "option, a local shell can be initialized. With h or help a short "
507 "help message about the usuage can be brought to the screen.\n"
508 "\n"
509 "Usage: fscctrl [-c type] [OPTIONS]\n"
510 " or: fscctrl [OPTIONS]\n";
511 cout << endl;
512}
513
514void PrintHelp()
515{
516 Main::PrintHelp<StateMachineAgilent<StateMachine, ConnectionAgilent>>();
517
518 /* Additional help text which is printed after the configuration
519 options goes here */
520
521 /*
522 cout << "bla bla bla" << endl << endl;
523 cout << endl;
524 cout << "Environment:" << endl;
525 cout << "environment" << endl;
526 cout << endl;
527 cout << "Examples:" << endl;
528 cout << "test exam" << endl;
529 cout << endl;
530 cout << "Files:" << endl;
531 cout << "files" << endl;
532 cout << endl;
533 */
534}
535
536int main(int argc, const char* argv[])
537{
538 Configuration conf(argv[0]);
539 conf.SetPrintUsage(PrintUsage);
540 Main::SetupConfiguration(conf);
541 SetupConfiguration(conf);
542
543 if (!conf.DoParse(argc, argv, PrintHelp))
544 return 127;
545
546 //try
547 {
548 // No console access at all
549 if (!conf.Has("console"))
550 {
551 if (conf.Get<bool>("no-dim"))
552 return RunShell<LocalStream, StateMachine, ConnectionAgilent>(conf);
553 else
554 return RunShell<LocalStream, StateMachineDim, ConnectionDimAgilent>(conf);
555 }
556 // Cosole access w/ and w/o Dim
557 if (conf.Get<bool>("no-dim"))
558 {
559 if (conf.Get<int>("console")==0)
560 return RunShell<LocalShell, StateMachine, ConnectionAgilent>(conf);
561 else
562 return RunShell<LocalConsole, StateMachine, ConnectionAgilent>(conf);
563 }
564 else
565 {
566 if (conf.Get<int>("console")==0)
567 return RunShell<LocalShell, StateMachineDim, ConnectionDimAgilent>(conf);
568 else
569 return RunShell<LocalConsole, StateMachineDim, ConnectionDimAgilent>(conf);
570 }
571 }
572 /*catch (std::exception& e)
573 {
574 cerr << "Exception: " << e.what() << endl;
575 return -1;
576 }*/
577
578 return 0;
579}
Note: See TracBrowser for help on using the repository browser.