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

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