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

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