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

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