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

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