source: trunk/FACT++/src/drivectrl.cc@ 16579

Last change on this file since 16579 was 15458, checked in by tbretz, 12 years ago
Connection can only be closed by cosy not the FTM ;)
File size: 51.4 KB
Line 
1#include <boost/bind.hpp>
2#include <boost/regex.hpp>
3
4#ifdef HAVE_SQL
5#include "Database.h"
6#endif
7
8#include "FACT.h"
9#include "Dim.h"
10#include "Event.h"
11#include "Shell.h"
12#include "StateMachineDim.h"
13#include "Connection.h"
14#include "LocalControl.h"
15#include "Configuration.h"
16#include "Timers.h"
17#include "Console.h"
18
19#include "HeadersDrive.h"
20
21namespace ba = boost::asio;
22namespace bs = boost::system;
23namespace dummy = ba::placeholders;
24
25using namespace std;
26using namespace Drive;
27
28// ------------------------------------------------------------------------
29
30class ConnectionDrive : public Connection
31{
32 int fState;
33
34 bool fIsVerbose;
35
36 // --verbose
37 // --hex-out
38 // --dynamic-out
39 // --load-file
40 // --leds
41 // --trigger-interval
42 // --physcis-coincidence
43 // --calib-coincidence
44 // --physcis-window
45 // --physcis-window
46 // --trigger-delay
47 // --time-marker-delay
48 // --dead-time
49 // --clock-conditioner-r0
50 // --clock-conditioner-r1
51 // --clock-conditioner-r8
52 // --clock-conditioner-r9
53 // --clock-conditioner-r11
54 // --clock-conditioner-r13
55 // --clock-conditioner-r14
56 // --clock-conditioner-r15
57 // ...
58
59 virtual void UpdatePointing(const Time &, const array<double, 2> &)
60 {
61 }
62
63 virtual void UpdateTracking(const Time &, const array<double, 8> &)
64 {
65 }
66
67 virtual void UpdateStatus(const Time &, const array<uint8_t, 3> &)
68 {
69 }
70
71 virtual void UpdateStarguider(const Time &, const DimStarguider &)
72 {
73 }
74
75 virtual void UpdateTPoint(const Time &, const DimTPoint &, const string &)
76 {
77 }
78
79public:
80 virtual void UpdateSource()
81 {
82 }
83 virtual void UpdateSource(const array<double, 6> &, const string& = "")
84 {
85 }
86
87protected:
88 map<uint16_t, int> fCounter;
89
90 ba::streambuf fBuffer;
91
92public:
93 static Time ReadTime(istream &in)
94 {
95 uint16_t y, m, d, hh, mm, ss, ms;
96 in >> y >> m >> d >> hh >> mm >> ss >> ms;
97
98 return Time(y, m, d, hh, mm, ss, ms*1000);
99 }
100
101 static double ReadAngle(istream &in)
102 {
103 char sgn;
104 uint16_t d, m;
105 float s;
106
107 in >> sgn >> d >> m >> s;
108
109 const double ret = ((60.0 * (60.0 * (double)d + (double)m) + s))/3600.;
110 return sgn=='-' ? -ret : ret;
111 }
112
113 double GetDevAbs(double nomzd, double meszd, double devaz)
114 {
115 nomzd *= M_PI/180;
116 meszd *= M_PI/180;
117 devaz *= M_PI/180;
118
119 const double x = sin(meszd) * sin(nomzd) * cos(devaz);
120 const double y = cos(meszd) * cos(nomzd);
121
122 return acos(x + y) * 180/M_PI;
123 }
124
125 uint16_t fDeviationLimit;
126 uint16_t fDeviationCounter;
127 uint16_t fDeviationMax;
128
129 uint64_t fTrackingCounter;
130
131 void ProcessDriveStatus(const string &line)
132 {
133 Message(line);
134 }
135
136 bool ProcessStargReport(const string &line)
137 {
138 istringstream stream(line);
139
140 // 0: Error
141 // 1: Standby
142 // 2: Monitoring
143 uint16_t status1;
144 stream >> status1;
145 /*const Time t1 = */ReadTime(stream);
146
147 uint16_t status2;
148 stream >> status2;
149 /*const Time t2 = */ReadTime(stream);
150
151 double misszd, missaz;
152 stream >> misszd >> missaz;
153
154 const double zd = ReadAngle(stream);
155 const double az = ReadAngle(stream);
156
157 double cx, cy;
158 stream >> cx >> cy;
159
160 int ncor;
161 stream >> ncor;
162
163 double bright, mjd;
164 stream >> bright >> mjd;
165
166 int nled, nring, nstars;
167 stream >> nled >> nring >> nstars;
168
169 if (stream.fail())
170 return false;
171
172 DimStarguider data;
173
174 data.fMissZd = misszd;
175 data.fMissAz = missaz;
176 data.fNominalZd = zd;
177 data.fNominalAz = az;
178 data.fCenterX = cx;
179 data.fCenterY = cy;
180 data.fNumCorrelated = ncor;
181 data.fBrightness = bright;
182 data.fNumLeds = nled;
183 data.fNumRings = nring;
184 data.fNumStars = nstars;
185
186 UpdateStarguider(Time(mjd), data);
187
188 return true;
189 }
190
191 bool ProcessTpointReport(const string &line)
192 {
193 istringstream stream(line);
194
195 uint16_t status1;
196 stream >> status1;
197 const Time t1 = ReadTime(stream);
198
199 uint16_t status2;
200 stream >> status2;
201 /*const Time t2 =*/ ReadTime(stream);
202
203 char type;
204 stream >> type;
205 if (type != 'T')
206 return false;
207
208 double az1, alt1, az2, alt2, ra, dec, dzd, daz;
209 stream >> az1 >> alt1 >> az2 >> alt2 >> ra >> dec >> dzd >> daz;
210
211 // c: center, s:start
212 double mjd, cmag, smag, cx, cy, sx, sy;
213 stream >> mjd >> cmag >> smag >> cx >> cy >> sx >> sy;
214
215 int nled, nring, nstar, ncor;
216 stream >> nled >> nring >> nstar >> ncor;
217
218 double bright, mag;
219 stream >> bright >> mag;
220
221 string name;
222 stream >> name;
223
224 if (stream.fail())
225 return false;
226
227 DimTPoint tpoint;
228
229 tpoint.fRa = ra;
230 tpoint.fDec = dec;
231
232 tpoint.fNominalZd = 90-alt1-dzd;
233 tpoint.fNominalAz = 90-az1 +daz;
234
235 tpoint.fPointingZd = 90-alt1;
236 tpoint.fPointingAz = az1;
237
238 tpoint.fFeedbackZd = 90-alt2;
239 tpoint.fFeedbackAz = az2;
240
241 tpoint.fNumLeds = nled;
242 tpoint.fNumRings = nring;
243
244 tpoint.fCenterX = cx;
245 tpoint.fCenterY = cy;
246 tpoint.fCenterMag = cmag;
247
248 tpoint.fStarX = sx;
249 tpoint.fStarY = sy;
250 tpoint.fStarMag = smag;
251
252 tpoint.fRealMag = mag;
253
254 UpdateTPoint(t1, tpoint, name);
255
256 return true;
257 }
258
259 bool ProcessDriveReport(const string &line)
260 {
261 // DRIVE-REPORT M1
262 // 01 2011 05 14 11 31 19 038
263 // 02 1858 11 17 00 00 00 000
264 // + 000 00 000 + 000 00 000
265 // + 000 00 000
266 // 55695.480081
267 // + 000 00 000 + 000 00 000
268 // + 000 00 000 + 000 00 000
269 // 0000.000 0000.000
270 // 0 2
271
272 // status
273 // year month day hour minute seconds millisec
274 // year month day hour minute seconds millisec
275 // ra(+ h m s) dec(+ d m s) ha(+ h m s)
276 // mjd
277 // zd(+ d m s) az(+ d m s)
278 // zd(+ d m s) az(+ d m s)
279 // zd_err az_err
280 // armed(0=unlocked, 1=locked)
281 // stgmd(0=none, 1=starguider, 2=starguider off)
282 istringstream stream(line);
283
284 uint16_t status1;
285 stream >> status1;
286 const Time t1 = ReadTime(stream);
287
288 uint16_t status2;
289 stream >> status2;
290 /*const Time t2 =*/ ReadTime(stream);
291
292 const double ra = ReadAngle(stream);
293 const double dec = ReadAngle(stream);
294 const double ha = ReadAngle(stream);
295
296 double mjd;
297 stream >> mjd;
298
299 const double zd1 = ReadAngle(stream); // Nominal (zd/az asynchronous, dev synchronous, mjd synchronous with zd)
300 const double az1 = ReadAngle(stream); // Nominal (zd/az asynchronous, dev synchronous, mjd synchronous with z)
301 const double zd2 = ReadAngle(stream); // Masured (zd/az synchronous, dev asynchronous, mjd asynchronous)
302 const double az2 = ReadAngle(stream); // Measurd (zd/az synchronous, dev asynchronous, mjd asynchronous)
303
304 double zd_err, az_err;
305 stream >> zd_err; // Deviation = Nominal - Measured
306 stream >> az_err; // Deviation = Nominal - Measured
307
308 uint16_t armed, stgmd;
309 stream >> armed;
310 stream >> stgmd;
311
312 uint32_t pdo3;
313 stream >> hex >> pdo3;
314
315 if (stream.fail())
316 return false;
317
318 // Status 0: Error
319 // Status 1: Stopped
320 // Status 3: Stopping || Moving
321 // Status 4: Tracking
322 if (status1==0)
323 status1 = StateMachineImp::kSM_Error - Drive::State::kNotReady;
324
325 const bool ready = (pdo3&0xef00ef)==0xef00ef;
326 if (!ready)
327 fState = Drive::State::kNotReady;
328 else
329 fState = status1==1 ?
330 Drive::State::kReady+armed :
331 Drive::State::kNotReady+status1;
332
333 // kDisconnected = 1,
334 // kConnected,
335 // kNotReady,
336 // kReady,
337 // kArmed,
338 // kMoving,
339 // kTracking,
340 // kOnTrack,
341
342 // pdo3:
343 // 1 Ab
344 // 2 1
345 // 4 Emergency
346 // 8 OverVolt
347 // 10 Move (Drehen-soll)
348 // 20 Af
349 // 40 1
350 // 80 Power on Az
351 // ------------------
352 // 100 NOT UPS Alarm
353 // 200 UPS on Battery
354 // 400 UPS charging
355
356 // Power cut: 2ef02ef
357 // charging: 4ef04ef
358
359 // Convert to deg
360 zd_err /= 3600;
361 az_err /= 3600;
362
363 // Calculate absolut deviation on the sky
364 const double dev = GetDevAbs(zd1, zd1-zd_err, az_err)*3600;
365
366 // If any other state than tracking or a deviation
367 // larger than 60, reset the counter
368 if (fState!=State::kTracking || dev>fDeviationLimit)
369 fTrackingCounter = 0;
370 else
371 fTrackingCounter++;
372
373 // If in tracking, at least five consecutive reports (5s)
374 // must be below 60arcsec deviation, this is considered OnTrack
375 if (fState==State::kTracking && fTrackingCounter>=fDeviationCounter)
376 fState = State::kOnTrack;
377
378 // Having th state as Tracking will reset the counter
379 if (fState==State::kOnTrack && dev>fDeviationMax)
380 fState = State::kTracking;
381
382 // 206 206 ce ce pwr vlt emcy fs | pwr vlt emcy fs
383 // 239 239 ef ef pwr vlt emcy fs bb rf | pwr vlt emcy fs bb rf
384 // 111 78 6f 4e vlt emcy fs bb rf | emcy fs bb
385
386 /*
387 fArmed = data[3]&0x01; // armed status
388 fPosActive = data[3]&0x02; // positioning active
389 fRpmActive = data[3]&0x04; // RPM mode switched on
390 // data[3]&0x08; // - unused -
391 // data[3]&0x10; // - unused -
392 // data[3]&0x20; // - unused -
393 //fInControl = data[3]&0x40; // motor uncontrolled
394 // data[3]&0x80; // axis resetted (after errclr, motor stop, motor on)
395
396 fStatus = data[3];
397 }
398
399 const LWORD_t stat = data[0] | (data[1]<<8);
400 if (fStatusPdo3!=stat)
401 {
402 gLog << inf << MTime(-1) << ": " << GetNodeName() << " - PDO3(0x" << hex << (int)stat << dec << ") = ";
403 const Bool_t ready = stat&0x001;
404 const Bool_t fuse = stat&0x002;
405 const Bool_t emcy = stat&0x004;
406 const Bool_t vltg = stat&0x008;
407 const Bool_t mode = stat&0x010;
408 const Bool_t rf = stat&0x020;
409 const Bool_t brake = stat&0x040;
410 const Bool_t power = stat&0x080;
411 const Bool_t alarm = stat&0x100; // UPS Alarm (FACT only)
412 const Bool_t batt = stat&0x200; // UPS on battery (FACT only)
413 const Bool_t charge = stat&0x400; // UPS charging (FACT only)
414 if (ready) gLog << "DKC-Ready ";
415 if (fuse) gLog << "FuseOk ";
416 if (emcy) gLog << "EmcyOk ";
417 if (vltg) gLog << "OvervoltOk ";
418 if (mode) gLog << "SwitchToManualMode ";
419 if (rf) gLog << "RF ";
420 if (brake) gLog << "BrakeOpen ";
421 if (power) gLog << "PowerOn ";
422 if (alarm) gLog << "UPS-PowerLoss ";
423 if (batt) gLog << "UPS-OnBattery ";
424 if (charge) gLog << "UPS-Charging ";
425 gLog << endl;
426
427 fStatusPdo3 = stat;
428 }*/
429
430 // ((stat1&0xffff)<<16)|(stat2&0xffff)
431 // no alarm, no batt, no charge
432 const array<uint8_t, 3> state = {{ uint8_t(pdo3>>16), uint8_t(pdo3), uint8_t(pdo3>>24) }};
433 UpdateStatus(t1, state);
434
435 const array<double, 2> point = {{ zd2, az2 }};
436 UpdatePointing(t1, point);
437
438 const array<double, 8> track =
439 {{
440 ra, dec, ha,
441 zd1, az1,
442 zd_err, az_err,
443 dev
444 }};
445 if (mjd>0)
446 UpdateTracking(Time(mjd), track);
447
448 // ---- DIM ----> t1 as event time
449 // status1
450 // mjd
451 // ra/dec/ha
452 // zd/az (nominal)
453 // zd/az (current)
454 // err(zd/az)
455 // [armed] [stgmd]
456
457 // Maybe:
458 // POINTING_POSITION --> t1, zd/az (current), [armed, stgmd, status1]
459 //
460 // if (mjd>0)
461 // TRACKING_POSITION --> mjd, zd/az (nominal), err(zd/az)
462 // ra/dec, ha(not well defined),
463 // [Nominal + Error == Current]
464
465 // MJD is the time which corresponds to the nominal position
466 // t1 is the time which corresponds to the current position/HA
467
468 return true;
469 }
470
471protected:
472 void HandleReceivedReport(const boost::system::error_code& err, size_t bytes_received)
473 {
474 // Do not schedule a new read if the connection failed.
475 if (bytes_received==0 || err)
476 {
477 if (err==ba::error::eof)
478 Warn("Connection closed by remote host (cosy).");
479
480 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
481 // 125: Operation canceled
482 if (err && err!=ba::error::eof && // Connection closed by remote host
483 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
484 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
485 {
486 ostringstream str;
487 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
488 Error(str);
489 }
490 PostClose(err!=ba::error::basic_errors::operation_aborted);
491 return;
492 }
493
494 istream is(&fBuffer);
495
496 string line;
497 getline(is, line);
498
499 if (fIsVerbose)
500 Out() << line << endl;
501
502 StartReadReport();
503
504 if (line.substr(0, 13)=="DRIVE-STATUS ")
505 {
506 ProcessDriveStatus(line.substr(70));
507 return;
508 }
509
510 if (line.substr(0, 13)=="STARG-REPORT ")
511 {
512 ProcessStargReport(line.substr(16));
513 return;
514 }
515
516 if (line.substr(0, 14)=="TPOINT-REPORT ")
517 {
518 ProcessTpointReport(line.substr(17));
519 return;
520 }
521
522 if (line.substr(0, 13)=="DRIVE-REPORT ")
523 {
524 ProcessDriveReport(line.substr(16));
525 return;
526 }
527 }
528
529 void StartReadReport()
530 {
531 boost::asio::async_read_until(*this, fBuffer, '\n',
532 boost::bind(&ConnectionDrive::HandleReceivedReport, this,
533 dummy::error, dummy::bytes_transferred));
534 }
535
536 boost::asio::deadline_timer fKeepAlive;
537
538 void KeepAlive()
539 {
540 PostMessage(string("KEEP_ALIVE"));
541
542 fKeepAlive.expires_from_now(boost::posix_time::seconds(10));
543 fKeepAlive.async_wait(boost::bind(&ConnectionDrive::HandleKeepAlive,
544 this, dummy::error));
545 }
546
547 void HandleKeepAlive(const bs::error_code &error)
548 {
549 // 125: Operation canceled (bs::error_code(125, bs::system_category))
550 if (error && error!=ba::error::basic_errors::operation_aborted)
551 {
552 ostringstream str;
553 str << "Write timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
554 Error(str);
555
556 PostClose(false);
557 return;
558 }
559
560 if (!is_open())
561 {
562 // For example: Here we could schedule a new accept if we
563 // would not want to allow two connections at the same time.
564 return;
565 }
566
567 // Check whether the deadline has passed. We compare the deadline
568 // against the current time since a new asynchronous operation
569 // may have moved the deadline before this actor had a chance
570 // to run.
571 if (fKeepAlive.expires_at() > ba::deadline_timer::traits_type::now())
572 return;
573
574 KeepAlive();
575 }
576
577
578private:
579 // This is called when a connection was established
580 void ConnectionEstablished()
581 {
582 StartReadReport();
583 KeepAlive();
584 }
585
586 /*
587 void HandleReadTimeout(const bs::error_code &error)
588 {
589 if (error && error!=ba::error::basic_errors::operation_aborted)
590 {
591 stringstream str;
592 str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
593 Error(str);
594
595 PostClose();
596 return;
597
598 }
599
600 if (!is_open())
601 {
602 // For example: Here we could schedule a new accept if we
603 // would not want to allow two connections at the same time.
604 return;
605 }
606
607 // Check whether the deadline has passed. We compare the deadline
608 // against the current time since a new asynchronous operation
609 // may have moved the deadline before this actor had a chance
610 // to run.
611 if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
612 return;
613
614 Error("Timeout reading data from "+URL());
615
616 PostClose();
617 }*/
618
619
620public:
621
622 static const uint16_t kMaxAddr;
623
624public:
625 ConnectionDrive(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
626 fState(-1), fIsVerbose(true), fDeviationLimit(120), fDeviationCounter(5), fDeviationMax(240), fTrackingCounter(0), fKeepAlive(ioservice)
627 {
628 SetLogStream(&imp);
629 }
630
631 void SetVerbose(bool b)
632 {
633 fIsVerbose = b;
634 }
635
636 void SetDeviationCondition(uint16_t limit, uint16_t counter, uint16_t max)
637 {
638 fDeviationLimit = limit;
639 fDeviationCounter = counter;
640 fDeviationMax = max;
641 }
642 int GetState() const
643 {
644 if (!IsConnected())
645 return 1;
646 if (IsConnected() && fState<0)
647 return 2;
648 return fState;
649 }
650};
651
652const uint16_t ConnectionkMaxAddr = 0xfff;
653
654// ------------------------------------------------------------------------
655
656#include "DimDescriptionService.h"
657
658class ConnectionDimDrive : public ConnectionDrive
659{
660private:
661 DimDescribedService fDimPointing;
662 DimDescribedService fDimTracking;
663 DimDescribedService fDimSource;
664 DimDescribedService fDimTPoint;
665 DimDescribedService fDimStatus;
666
667 void UpdatePointing(const Time &t, const array<double, 2> &arr)
668 {
669 fDimPointing.setData(arr);
670 fDimPointing.Update(t);
671 }
672
673 void UpdateTracking(const Time &t,const array<double, 8> &arr)
674 {
675 fDimTracking.setData(arr);
676 fDimTracking.Update(t);
677 }
678
679 void UpdateStatus(const Time &t, const array<uint8_t, 3> &arr)
680 {
681 fDimStatus.setData(arr);
682 fDimStatus.Update(t);
683 }
684
685 void UpdateTPoint(const Time &t, const DimTPoint &data,
686 const string &name)
687 {
688 vector<char> dim(sizeof(data)+name.length()+1);
689 memcpy(dim.data(), &data, sizeof(data));
690 memcpy(dim.data()+sizeof(data), name.c_str(), name.length()+1);
691
692 fDimTPoint.setData(dim);
693 fDimTPoint.Update(t);
694 }
695
696public:
697 ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) :
698 ConnectionDrive(ioservice, imp),
699 fDimPointing("DRIVE_CONTROL/POINTING_POSITION", "D:1;D:1",
700 "|Zd[deg]:Zenith distance (encoder readout)"
701 "|Az[deg]:Azimuth angle (encoder readout)"),
702 fDimTracking("DRIVE_CONTROL/TRACKING_POSITION", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1",
703 "|Ra[h]:Command right ascension"
704 "|Dec[deg]:Command declination"
705 "|Ha[h]:Corresponding hour angle"
706 "|Zd[deg]:Nominal zenith distance"
707 "|Az[deg]:Nominal azimuth angle"
708 "|dZd[deg]:Control deviation Zd"
709 "|dAz[deg]:Control deviation Az"
710 "|dev[arcsec]:Absolute control deviation"),
711 fDimSource("DRIVE_CONTROL/SOURCE_POSITION", "D:1;D:1;D:1;D:1;D:1;D:1;C:31",
712 "|Ra_src[h]:Source right ascension"
713 "|Dec_src[deg]:Source declination"
714 "|Ra_cmd[h]:Command right ascension"
715 "|Dec_cmd[deg]:Command declination"
716 "|Offset[deg]:Wobble offset"
717 "|Angle[deg]:Wobble angle"
718 "|Name[string]:Source name if available"),
719 fDimTPoint("DRIVE_CONTROL/TPOINT", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;S:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;C",
720 "|Ra[h]:Command right ascension"
721 "|Dec[deg]:Command declination"
722 "|Zd_nom[deg]:Nominal zenith distance"
723 "|Az_nom[deg]:Nominal azimuth angle"
724 "|Zd_cur[deg]:Current zenith distance (calculated from image)"
725 "|Az_cur[deg]:Current azimuth angle (calculated from image)"
726 "|Zd_enc[deg]:Feedback zenith axis (from encoder)"
727 "|Az_enc[deg]:Feedback azimuth angle (from encoder)"
728 "|N_leds[cnt]:Number of detected LEDs"
729 "|N_rings[cnt]:Number of rings used to calculate the camera center"
730 "|Xc[pix]:X position of center in CCD camera frame"
731 "|Yc[pix]:Y position of center in CCD camera frame"
732 "|Ic[au]:Average intensity (LED intensity weighted with their frequency of occurance in the calculation)"
733 "|Xs[pix]:X position of start in CCD camera frame"
734 "|Ys[pix]:Y position of star in CCD camera frame"
735 "|Ms[mag]:Artifical magnitude of star (calculated form image))"
736 "|Mc[mag]:Catalog magnitude of star"),
737 fDimStatus("DRIVE_CONTROL/STATUS", "C:2;C:1", "")
738
739 {
740 }
741
742 void UpdateSource()
743 {
744 const vector<char> empty(6*sizeof(double)+31, 0);
745 fDimSource.setQuality(0);
746 fDimSource.Update(empty);
747 }
748
749 void UpdateSource(const array<double, 6> &arr, const string &name="")
750 {
751 vector<char> dat(6*sizeof(double)+31, 0);
752 memcpy(dat.data(), arr.data(), 6*sizeof(double));
753 strncpy(dat.data()+6*sizeof(double), name.c_str(), 30);
754
755 fDimSource.setQuality(1);
756 fDimSource.Update(dat);
757 }
758
759 // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z
760};
761
762// ------------------------------------------------------------------------
763
764struct Source
765{
766 Source() : ra(0), dec(0), offset(0)
767 {
768 angle[0] = -90;
769 angle[1] = 90;
770 }
771
772 double ra;
773 double dec;
774 double offset;
775 array<double, 2> angle;
776};
777
778
779template <class T, class S>
780class StateMachineDrive : public T, public ba::io_service, public ba::io_service::work
781{
782private:
783 S fDrive;
784
785 string fDatabase;
786
787 typedef map<string, Source> sources;
788 sources fSources;
789
790 string fLastCommand; // Last tracking (RADEC) command
791 int fAutoResume; // 0: disabled, 1: enables, 2: resuming
792
793 // Status 0: Error
794 // Status 1: Unlocked
795 // Status 2: Locked
796 // Status 3: Stopping || Moving
797 // Status 4: Tracking
798
799 bool CheckEventSize(size_t has, const char *name, size_t size)
800 {
801 if (has==size)
802 return true;
803
804 ostringstream msg;
805 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
806 T::Fatal(msg);
807 return false;
808 }
809
810 enum Coordinates
811 {
812 kPoint,
813 kTrackSlow,
814 kTrackFast
815 };
816
817 string AngleToStr(double angle)
818 {
819 /* Handle sign */
820 const char sgn = angle<0?'-':'+';
821
822 /* Round interval and express in smallest units required */
823 double a = round(3600. * fabs(angle)); // deg to seconds
824
825 /* Separate into fields */
826 const double ad = trunc(a/3600.);
827 a -= ad * 3600.;
828 const double am = trunc(a/60.);
829 a -= am * 60.;
830 const double as = trunc(a);
831
832 /* Return results */
833 ostringstream str;
834 str << sgn << " " << uint16_t(ad) << " " << uint16_t(am) << " " << as;
835 return str.str();
836 }
837
838 int SendCommand(const string &str, bool upd=true)
839 {
840 // This happens if fLastCommand should be send,
841 // but the last command was not a tracking command
842 if (str.empty())
843 {
844 T::Info("Last command was not a tracking command. RESUME ignored.");
845 return T::GetCurrentState();
846 }
847
848 fLastCommand = str.compare(0, 6, "RADEC ")==0 ? str : "";
849
850 fDrive.PostMessage(str);
851 T::Message("Sending: "+str);
852
853 if (upd)
854 fDrive.UpdateSource();
855
856 return T::GetCurrentState();
857 }
858
859 int Park()
860 {
861 SendCommand("PREPS Park", false);
862
863 // FIXME: Go to locked state only when park position properly reached
864 return Drive::State::kLocked;
865 }
866
867 int Resume()
868 {
869 if (fLastCommand.empty())
870 {
871 T::Info("Last command was not a tracking command. RESUME ignored.");
872 return T::GetCurrentState();
873 }
874
875 return SendCommand(fLastCommand, false);
876 }
877
878 int SendCoordinates(const EventImp &evt, const Coordinates type)
879 {
880 if (!CheckEventSize(evt.GetSize(), "SendCoordinates", 16))
881 return T::kSM_FatalError;
882
883 const double *dat = evt.Ptr<double>();
884
885 string command;
886
887 switch (type)
888 {
889 case kPoint: command += "ZDAZ "; break;
890 case kTrackSlow: command += "RADEC "; break;
891 case kTrackFast: command += "GRB "; break;
892 }
893
894 if (type!=kPoint)
895 {
896 const array<double, 6> dim = {{ dat[0], dat[1], dat[0], dat[1], 0, 0 }};
897 fDrive.UpdateSource(dim);
898 }
899
900 command += AngleToStr(dat[0]) + ' ' + AngleToStr(dat[1]);
901 return SendCommand(command, type==kPoint);
902 }
903
904 int StartWobble(const double &srcra, const double &srcdec,
905 const double &woboff, const double &wobang,
906 const string name="")
907 {
908 const double ra = srcra *M_PI/12;
909 const double dec = srcdec*M_PI/180;
910 const double off = woboff*M_PI/180;
911 const double dir = wobang*M_PI/180;
912
913 const double cosdir = cos(dir);
914 const double sindir = sin(dir);
915 const double cosoff = cos(off);
916 const double sinoff = sin(off);
917 const double cosdec = cos(dec);
918 const double sindec = sin(dec);
919
920 if (off==0)
921 {
922 const array<double, 6> dim = {{ srcra, srcdec, srcra, srcdec, 0, 0 }};
923 fDrive.UpdateSource(dim, name);
924
925 string command = "RADEC ";
926 command += AngleToStr(srcra) + ' ' + AngleToStr(srcdec);
927 return SendCommand(command, false);
928 }
929
930 const double sintheta = sindec*cosoff + cosdec*sinoff*cosdir;
931 if (sintheta >= 1)
932 {
933 T::Error("cos(Zd) > 1");
934 return T::GetCurrentState();
935 }
936
937 const double costheta = sqrt(1 - sintheta*sintheta);
938
939 const double cosdeltara = (cosoff - sindec*sintheta)/(cosdec*costheta);
940 const double sindeltara = sindir*sinoff/costheta;
941
942 const double ndec = asin(sintheta)*180/M_PI;
943 const double nra = (atan2(sindeltara, cosdeltara) + ra)*12/M_PI;
944
945 const array<double, 6> dim = {{ srcra, srcdec, nra, ndec, woboff, wobang }};
946 fDrive.UpdateSource(dim, name);
947
948 string command = "RADEC ";
949 command += AngleToStr(nra) + ' ' + AngleToStr(ndec);
950 return SendCommand(command, false);
951 }
952
953 int Wobble(const EventImp &evt)
954 {
955 if (!CheckEventSize(evt.GetSize(), "Wobble", 32))
956 return T::kSM_FatalError;
957
958 const double *dat = evt.Ptr<double>();
959
960 return StartWobble(dat[0], dat[1], dat[2], dat[3]);
961 }
962
963 const sources::const_iterator GetSourceFromDB(const char *ptr, const char *last)
964 {
965 if (find(ptr, last, '\0')==last)
966 {
967 T::Fatal("TrackWobble - The name transmitted by dim is not null-terminated.");
968 throw uint32_t(T::kSM_FatalError);
969 }
970
971 const string name(ptr);
972
973 const sources::const_iterator it = fSources.find(name);
974 if (it==fSources.end())
975 {
976 T::Error("Source '"+name+"' not found in list.");
977 throw uint32_t(T::GetCurrentState());
978 }
979
980 return it;
981 }
982
983 int TrackWobble(const EventImp &evt)
984 {
985 if (evt.GetSize()<=2)
986 {
987 ostringstream msg;
988 msg << "Track - Received event has " << evt.GetSize() << " bytes, but expected at least 3.";
989 T::Fatal(msg);
990 return T::kSM_FatalError;
991 }
992
993 const uint16_t wobble = evt.GetUShort();
994 if (wobble!=1 && wobble!=2)
995 {
996 ostringstream msg;
997 msg << "TrackWobble - Wobble id " << wobble << " undefined, only 1 and 2 allowed.";
998 T::Error(msg);
999 return T::GetCurrentState();
1000 }
1001
1002 const char *ptr = evt.Ptr<char>(2);
1003 const char *last = ptr+evt.GetSize()-2;
1004
1005 try
1006 {
1007 const sources::const_iterator it = GetSourceFromDB(ptr, last);
1008
1009 const string &name = it->first;
1010 const Source &src = it->second;
1011
1012 return StartWobble(src.ra, src.dec, src.offset, src.angle[wobble-1], name);
1013 }
1014 catch (const uint32_t &e)
1015 {
1016 return e;
1017 }
1018 }
1019
1020 int StartTrackWobble(const char *ptr, size_t size, const double &offset=0, const double &angle=0)
1021 {
1022 const char *last = ptr+size;
1023
1024 try
1025 {
1026 const sources::const_iterator it = GetSourceFromDB(ptr, last);
1027
1028 const string &name = it->first;
1029 const Source &src = it->second;
1030
1031 return StartWobble(src.ra, src.dec, offset, angle, name);
1032 }
1033 catch (const uint32_t &e)
1034 {
1035 return e;
1036 }
1037
1038 }
1039
1040 int Track(const EventImp &evt)
1041 {
1042 if (evt.GetSize()<=16)
1043 {
1044 ostringstream msg;
1045 msg << "Track - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
1046 T::Fatal(msg);
1047 return T::kSM_FatalError;
1048 }
1049
1050 const double *dat = evt.Ptr<double>();
1051 const char *ptr = evt.Ptr<char>(16);
1052 const size_t size = evt.GetSize()-16;
1053
1054 return StartTrackWobble(ptr, size, dat[0], dat[1]);
1055 }
1056
1057 int TrackOn(const EventImp &evt)
1058 {
1059 if (evt.GetSize()==0)
1060 {
1061 ostringstream msg;
1062 msg << "TrackOn - Received event has " << evt.GetSize() << " bytes, but expected at least 1.";
1063 T::Fatal(msg);
1064 return T::kSM_FatalError;
1065 }
1066
1067 return StartTrackWobble(evt.Ptr<char>(), evt.GetSize());
1068 }
1069
1070
1071 int TakeTPoint(const EventImp &evt)
1072 {
1073 if (evt.GetSize()<=4)
1074 {
1075 ostringstream msg;
1076 msg << "TakePoint - Received event has " << evt.GetSize() << " bytes, but expected at least 5.";
1077 T::Fatal(msg);
1078 return T::kSM_FatalError;
1079 }
1080
1081 const float mag = evt.Get<float>();
1082 const char *ptr = evt.Ptr<char>(4);
1083 const size_t size = evt.GetSize()-4;
1084
1085 string src(ptr, size);
1086
1087 while (src.find_first_of(' '))
1088 src.erase(src.find_first_of(' '), 1);
1089
1090 SendCommand("TPOIN "+src+" "+to_string(mag), false);;
1091
1092 return T::GetCurrentState();
1093 }
1094
1095 int SetLedBrightness(const EventImp &evt)
1096 {
1097 if (!CheckEventSize(evt.GetSize(), "SetLedBrightness", 8))
1098 return T::kSM_FatalError;
1099
1100 const uint32_t *led = evt.Ptr<uint32_t>();
1101
1102 ostringstream cmd;
1103 cmd << "LEDS " << led[0] << " " << led[1];
1104 return SendCommand(cmd.str(), false);
1105 }
1106
1107
1108 int SetVerbosity(const EventImp &evt)
1109 {
1110 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
1111 return T::kSM_FatalError;
1112
1113 fDrive.SetVerbose(evt.GetBool());
1114
1115 return T::GetCurrentState();
1116 }
1117
1118 int SetAutoResume(const EventImp &evt)
1119 {
1120 if (!CheckEventSize(evt.GetSize(), "SetAutoResume", 1))
1121 return T::kSM_FatalError;
1122
1123 fAutoResume = evt.GetBool();
1124
1125 return T::GetCurrentState();
1126 }
1127
1128 int Unlock()
1129 {
1130 return Drive::State::kNotReady;
1131 }
1132
1133 int Print()
1134 {
1135 for (auto it=fSources.begin(); it!=fSources.end(); it++)
1136 {
1137 const string &name = it->first;
1138 const Source &src = it->second;
1139
1140 T::Out() << name << ",";
1141 T::Out() << src.ra << "," << src.dec << "," << src.offset << ",";
1142 T::Out() << src.angle[0] << "," << src.angle[1] << endl;
1143 }
1144 return T::GetCurrentState();
1145 }
1146
1147 int ReloadSources()
1148 {
1149 try
1150 {
1151 ReadDatabase();
1152 }
1153 catch (const exception &e)
1154 {
1155 T::Error("Reading sources from databse failed: "+string(e.what()));
1156 }
1157 return T::GetCurrentState();
1158 }
1159
1160 int Disconnect()
1161 {
1162 // Close all connections
1163 fDrive.PostClose(false);
1164
1165 /*
1166 // Now wait until all connection have been closed and
1167 // all pending handlers have been processed
1168 poll();
1169 */
1170
1171 return T::GetCurrentState();
1172 }
1173
1174 int Reconnect(const EventImp &evt)
1175 {
1176 // Close all connections to supress the warning in SetEndpoint
1177 fDrive.PostClose(false);
1178
1179 // Now wait until all connection have been closed and
1180 // all pending handlers have been processed
1181 poll();
1182
1183 if (evt.GetBool())
1184 fDrive.SetEndpoint(evt.GetString());
1185
1186 // Now we can reopen the connection
1187 fDrive.PostClose(true);
1188
1189 return T::GetCurrentState();
1190 }
1191
1192 Time fSunRise;
1193 /*
1194 int ShiftSunRise()
1195 {
1196 const Time sunrise = fSunRise;
1197
1198 fSunRise = Time().GetNextSunRise();
1199
1200 if (sunrise==fSunRise)
1201 return Drive::State::kLocked;
1202
1203 ostringstream msg;
1204 msg << "Next sun-rise will be at " << fSunRise;
1205 T::Info(msg);
1206
1207 return Drive::State::kLocked;
1208 }*/
1209
1210 int Execute()
1211 {
1212 // Dispatch (execute) at most one handler from the queue. In contrary
1213 // to run_one(), it doesn't wait until a handler is available
1214 // which can be dispatched, so poll_one() might return with 0
1215 // handlers dispatched. The handlers are always dispatched/executed
1216 // synchronously, i.e. within the call to poll_one()
1217 poll_one();
1218
1219 /*
1220 if (T::GetCurrentState()==Drive::State::kLocked)
1221 return ShiftSunRise();
1222
1223 if (T::GetCurrentState()>Drive::State::kLocked)
1224 {
1225 if (Time()>fSunRise)
1226 return Park();
1227 }*/
1228
1229 if (Time()>fSunRise)
1230 {
1231 if (T::GetCurrentState()>Drive::State::kLocked)
1232 return Park();
1233
1234 if (T::GetCurrentState()==Drive::State::kLocked)
1235 {
1236 fSunRise = Time().GetNextSunRise();
1237
1238 ostringstream msg;
1239 msg << "Next sun-rise will be at " << fSunRise;
1240 T::Info(msg);
1241
1242 return Drive::State::kLocked;
1243 }
1244 }
1245
1246 if (T::GetCurrentState()==Drive::State::kLocked)
1247 return Drive::State::kLocked;
1248
1249 const int state = fDrive.GetState();
1250
1251 if (!fLastCommand.empty())
1252 {
1253 // If auto resume is enabled and the drive is in error,
1254 // resume tracking
1255 if (fAutoResume==1 && state==StateMachineImp::kSM_Error)
1256 {
1257 Resume();
1258 fAutoResume = 2;
1259 }
1260
1261 // If drive got out of the error state,
1262 // enable auto resume again
1263 if (fAutoResume==2 && state!=StateMachineImp::kSM_Error)
1264 fAutoResume = 1;
1265 }
1266
1267 return state;
1268 }
1269
1270
1271public:
1272 StateMachineDrive(ostream &out=cout) :
1273 T(out, "DRIVE_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
1274 fDrive(*this, *this), fAutoResume(false), fSunRise(Time().GetNextSunRise())
1275 {
1276 // State names
1277 T::AddStateName(State::kDisconnected, "Disconnected",
1278 "No connection to cosy");
1279
1280 T::AddStateName(State::kConnected, "Connected",
1281 "Cosy connected, drive stopped");
1282
1283 T::AddStateName(State::kNotReady, "NotReady",
1284 "Drive system not ready for movement");
1285
1286 T::AddStateName(State::kLocked, "Locked",
1287 "Drive system is locked (will not accept commands)");
1288
1289 T::AddStateName(State::kReady, "Ready",
1290 "Drive system ready for movement");
1291
1292 T::AddStateName(State::kArmed, "Armed",
1293 "Cosy armed, drive stopped");
1294
1295 T::AddStateName(State::kMoving, "Moving",
1296 "Telescope moving");
1297
1298 T::AddStateName(State::kTracking, "Tracking",
1299 "Telescope is in tracking mode");
1300
1301 T::AddStateName(State::kOnTrack, "OnTrack",
1302 "Telescope tracking stable");
1303
1304 // State::kIdle
1305 // State::kArmed
1306 // State::kMoving
1307 // State::kTracking
1308
1309 // Init
1310 // -----------
1311 // "ARM lock"
1312 // "STGMD off"
1313
1314 /*
1315 [ ] WAIT -> WM_WAIT
1316 [x] STOP! -> WM_STOP
1317 [x] RADEC ra(+ d m s.f) dec(+ d m s.f)
1318 [x] GRB ra(+ d m s.f) dec(+ d m s.f)
1319 [x] ZDAZ zd(+ d m s.f) az (+ d m s.f)
1320 [ ] CELEST id offset angle
1321 [ ] MOON wobble offset
1322 [ ] PREPS string
1323 [ ] TPOIN star mag
1324 [ ] ARM lock/unlock
1325 [ ] STGMD on/off
1326 */
1327
1328 // Drive Commands
1329 T::AddEvent("MOVE_TO", "D:2", State::kArmed) // ->ZDAZ
1330 (bind(&StateMachineDrive::SendCoordinates, this, placeholders::_1, kPoint))
1331 ("Move the telescope to the given local coordinates"
1332 "|Zd[deg]:Zenith distance"
1333 "|Az[deg]:Azimuth");
1334
1335 T::AddEvent("TRACK", "D:2", State::kArmed, State::kTracking, State::kOnTrack) // ->RADEC/GRB
1336 (bind(&StateMachineDrive::SendCoordinates, this, placeholders::_1, kTrackSlow))
1337 ("Move the telescope to the given sky coordinates and start tracking them"
1338 "|Ra[h]:Right ascension"
1339 "|Dec[deg]:Declination");
1340
1341 T::AddEvent("WOBBLE", "D:4", State::kArmed, State::kTracking, State::kOnTrack) // ->RADEC/GRB
1342 (bind(&StateMachineDrive::Wobble, this, placeholders::_1))
1343 ("Move the telescope to the given wobble position around the given sky coordinates and start tracking them"
1344 "|Ra[h]:Right ascension"
1345 "|Dec[deg]:Declination"
1346 "|Offset[deg]:Wobble offset"
1347 "|Angle[deg]:Wobble angle");
1348
1349 T::AddEvent("TRACK_SOURCE", "D:2;C", State::kArmed, State::kTracking, State::kOnTrack) // ->RADEC/GRB
1350 (bind(&StateMachineDrive::Track, this, placeholders::_1))
1351 ("Move the telescope to the given wobble position around the given source and start tracking"
1352 "|Offset[deg]:Wobble offset"
1353 "|Angle[deg]:Wobble angle"
1354 "|Name[string]:Source name");
1355
1356 T::AddEvent("TRACK_WOBBLE", "S:1;C", State::kArmed, State::kTracking, State::kOnTrack) // ->RADEC/GRB
1357 (bind(&StateMachineDrive::TrackWobble, this, placeholders::_1))
1358 ("Move the telescope to the given wobble position around the given source and start tracking"
1359 "|id:Wobble angle id (1 or 2)"
1360 "|Name[string]:Source name");
1361
1362 T::AddEvent("TRACK_ON", "C", State::kArmed, State::kTracking, State::kOnTrack) // ->RADEC/GRB
1363 (bind(&StateMachineDrive::TrackOn, this, placeholders::_1))
1364 ("Move the telescope to the given position and start tracking"
1365 "|Name[string]:Source name");
1366
1367 T::AddEvent("RESUME", StateMachineImp::kSM_Error)
1368 (bind(&StateMachineDrive::Resume, this))
1369 ("If drive is in Error state, this can b used to resume the last tracking command, if the last command sent to cosy was a tracking command.");
1370
1371 T::AddEvent("MOON", State::kArmed, State::kTracking, State::kOnTrack)
1372 (bind(&StateMachineDrive::SendCommand, this, "MOON 0 0", true))
1373 ("Start tracking the moon");
1374 T::AddEvent("VENUS", State::kArmed, State::kTracking, State::kOnTrack)
1375 (bind(&StateMachineDrive::SendCommand, this, "CELEST 2 0 0", true))
1376 ("Start tracking Venus");
1377 T::AddEvent("MARS", State::kArmed, State::kTracking, State::kOnTrack)
1378 (bind(&StateMachineDrive::SendCommand, this, "CELEST 4 0 0", true))
1379 ("Start tracking Mars");
1380 T::AddEvent("JUPITER", State::kArmed, State::kTracking, State::kOnTrack)
1381 (bind(&StateMachineDrive::SendCommand, this, "CELEST 5 0 0", true))
1382 ("Start tracking Jupiter");
1383 T::AddEvent("SATURN", State::kArmed, State::kTracking, State::kOnTrack)
1384 (bind(&StateMachineDrive::SendCommand, this, "CELEST 6 0 0", true))
1385 ("Start tracking Saturn");
1386
1387 T::AddEvent("PARK", State::kArmed, State::kMoving, State::kTracking, State::kOnTrack, 0x100)
1388 (bind(&StateMachineDrive::Park, this))
1389 ("Park the telescope");
1390
1391 T::AddEvent("TAKE_TPOINT")
1392 (bind(&StateMachineDrive::SendCommand, this, "TPOIN FACT 0", false))
1393 ("Take a TPoint");
1394
1395 T::AddEvent("TPOINT", "F:1;C")
1396 (bind(&StateMachineDrive::TakeTPoint, this, placeholders::_1))
1397 ("Take a TPoint (given values will be written to the TPoint files)"
1398 "|mag[float]:Magnitude of the star"
1399 "|name[string]:Name of the star");
1400
1401 T::AddEvent("SET_LED_BRIGHTNESS", "I:2")
1402 (bind(&StateMachineDrive::SetLedBrightness, this, placeholders::_1))
1403 ("Set the LED brightness of the top and bottom leds"
1404 "|top[au]:Allowed range 0-32767 for top LEDs"
1405 "|bot[au]:Allowed range 0-32767 for bottom LEDs");
1406
1407 T::AddEvent("LEDS_OFF")
1408 (bind(&StateMachineDrive::SendCommand, this, "LEDS 0 0", false))
1409 ("Switch off TPoint LEDs");
1410
1411 T::AddEvent("STOP")
1412 (bind(&StateMachineDrive::SendCommand, this, "STOP!", true))
1413 ("Stop any kind of movement.");
1414
1415// T::AddEvent("ARM", State::kConnected)
1416// (bind(&StateMachineSendCommand, this, "ARM lock"))
1417// ("");
1418
1419 T::AddEvent("UNLOCK", Drive::State::kLocked)
1420 (bind(&StateMachineDrive::Unlock, this))
1421 ("Unlock locked state.");
1422
1423 T::AddEvent("SET_AUTORESUME", "B")
1424 (bind(&StateMachineDrive::SetAutoResume, this, placeholders::_1))
1425 ("Enable/disable auto resume"
1426 "|resume[bool]:if enabled, drive is tracking and goes to error state, the last tracking command is repeated automatically.");
1427
1428 // Verbosity commands
1429 T::AddEvent("SET_VERBOSE", "B")
1430 (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1))
1431 ("Set verbosity state"
1432 "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");
1433
1434 // Conenction commands
1435 T::AddEvent("DISCONNECT", State::kConnected, State::kArmed)
1436 (bind(&StateMachineDrive::Disconnect, this))
1437 ("disconnect from ethernet");
1438
1439 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected, State::kArmed)
1440 (bind(&StateMachineDrive::Reconnect, this, placeholders::_1))
1441 ("(Re)connect ethernet connection to FTM, a new address can be given"
1442 "|[host][string]:new ethernet address in the form <host:port>");
1443
1444
1445 T::AddEvent("PRINT")
1446 (bind(&StateMachineDrive::Print, this))
1447 ("Print source list.");
1448
1449 T::AddEvent("RELOAD_SOURCES")
1450 (bind(&StateMachineDrive::ReloadSources, this))
1451 ("Reload sources from database after database has changed..");
1452
1453 fDrive.StartConnect();
1454 }
1455
1456 void SetEndpoint(const string &url)
1457 {
1458 fDrive.SetEndpoint(url);
1459 }
1460
1461 bool AddSource(const string &name, const Source &src)
1462 {
1463 const auto it = fSources.find(name);
1464 if (it!=fSources.end())
1465 T::Warn("Source '"+name+"' already in list... overwriting.");
1466
1467 fSources[name] = src;
1468 return it==fSources.end();
1469 }
1470
1471 void ReadDatabase(bool print=true)
1472 {
1473#ifdef HAVE_SQL
1474 Database db(fDatabase);
1475
1476 T::Message("Connected to '"+db.uri()+"'");
1477
1478 const mysqlpp::StoreQueryResult res =
1479 db.query("SELECT fSourceName, fRightAscension, fDeclination, fWobbleOffset, fWobbleAngle0, fWobbleAngle1 FROM source").store();
1480
1481 fSources.clear();
1482 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
1483 {
1484 const string name = (*v)[0].c_str();
1485
1486 Source src;
1487 src.ra = (*v)[1];
1488 src.dec = (*v)[2];
1489 src.offset = (*v)[3];
1490 src.angle[0] = (*v)[4];
1491 src.angle[1] = (*v)[5];
1492 AddSource(name, src);
1493
1494 if (!print)
1495 continue;
1496
1497 ostringstream msg;
1498 msg << " " << name << setprecision(8) << ": Ra=" << src.ra << "h Dec=" << src.dec << "deg";
1499 msg << " Wobble=[" << src.offset << "," << src.angle[0] << "," << src.angle[1] << "]";
1500 T::Message(msg);
1501 }
1502#else
1503 T::Warn("MySQL support not compiled into the program.");
1504#endif
1505 }
1506
1507 int EvalOptions(Configuration &conf)
1508 {
1509 if (!fSunRise)
1510 return 1;
1511
1512 fDrive.SetVerbose(!conf.Get<bool>("quiet"));
1513
1514 const vector<string> &vec = conf.Vec<string>("source");
1515
1516 for (vector<string>::const_iterator it=vec.begin(); it!=vec.end(); it++)
1517 {
1518 istringstream stream(*it);
1519
1520 string name;
1521
1522 int i=0;
1523
1524 Source src;
1525
1526 string buffer;
1527 while (getline(stream, buffer, ','))
1528 {
1529 istringstream is(buffer);
1530
1531 switch (i++)
1532 {
1533 case 0: name = buffer; break;
1534 case 1: src.ra = ConnectionDrive::ReadAngle(is); break;
1535 case 2: src.dec = ConnectionDrive::ReadAngle(is); break;
1536 case 3: is >> src.offset; break;
1537 case 4: is >> src.angle[0]; break;
1538 case 5: is >> src.angle[1]; break;
1539 }
1540
1541 if (is.fail())
1542 break;
1543 }
1544
1545 if (i==3 || i==6)
1546 {
1547 AddSource(name, src);
1548 continue;
1549 }
1550
1551 T::Warn("Resource 'source' not correctly formatted: '"+*it+"'");
1552 }
1553
1554 fDrive.SetDeviationCondition(conf.Get<uint16_t>("deviation-limit"),
1555 conf.Get<uint16_t>("deviation-count"),
1556 conf.Get<uint16_t>("deviation-max"));
1557
1558 fAutoResume = conf.Get<bool>("auto-resume");
1559
1560 if (conf.Has("source-database"))
1561 {
1562 fDatabase = conf.Get<string>("source-database");
1563 ReadDatabase();
1564 }
1565
1566 if (fSunRise.IsValid())
1567 {
1568 ostringstream msg;
1569 msg << "Next sun-rise will be at " << fSunRise;
1570 T::Message(msg);
1571 }
1572
1573 // The possibility to connect should be last, so that
1574 // everything else is already initialized.
1575 SetEndpoint(conf.Get<string>("addr"));
1576
1577 return -1;
1578 }
1579};
1580
1581// ------------------------------------------------------------------------
1582
1583#include "Main.h"
1584
1585
1586template<class T, class S, class R>
1587int RunShell(Configuration &conf)
1588{
1589 return Main::execute<T, StateMachineDrive<S, R>>(conf);
1590}
1591
1592void SetupConfiguration(Configuration &conf)
1593{
1594 const string def = "localhost:7404";
1595
1596 po::options_description control("Drive control options");
1597 control.add_options()
1598 ("no-dim,d", po_switch(), "Disable dim services")
1599 ("addr,a", var<string>(def), "Network address of cosy")
1600 ("quiet,q", po_bool(true), "Disable printing contents of all received messages (except dynamic data) in clear text.")
1601 ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
1602 ("source", vars<string>(), "Additional source entry in the form \"name,hh:mm:ss,dd:mm:ss\"")
1603 ("deviation-limit", var<uint16_t>(90), "Deviation limit in arcsec to get 'OnTrack'")
1604 ("deviation-count", var<uint16_t>(3), "Minimum number of reported deviation below deviation-limit to get 'OnTrack'")
1605 ("deviation-max", var<uint16_t>(180), "Maximum deviation in arcsec allowed to keep status 'OnTrack'")
1606 ("auto-resume", po_bool(false), "Enable auto result during tracking if connection is lost")
1607 ;
1608
1609 conf.AddOptions(control);
1610}
1611
1612/*
1613 Extract usage clause(s) [if any] for SYNOPSIS.
1614 Translators: "Usage" and "or" here are patterns (regular expressions) which
1615 are used to match the usage synopsis in program output. An example from cp
1616 (GNU coreutils) which contains both strings:
1617 Usage: cp [OPTION]... [-T] SOURCE DEST
1618 or: cp [OPTION]... SOURCE... DIRECTORY
1619 or: cp [OPTION]... -t DIRECTORY SOURCE...
1620 */
1621void PrintUsage()
1622{
1623 cout <<
1624 "The drivectrl is an interface to cosy.\n"
1625 "\n"
1626 "The default is that the program is started without user intercation. "
1627 "All actions are supposed to arrive as DimCommands. Using the -c "
1628 "option, a local shell can be initialized. With h or help a short "
1629 "help message about the usuage can be brought to the screen.\n"
1630 "\n"
1631 "Usage: drivectrl [-c type] [OPTIONS]\n"
1632 " or: drivectrl [OPTIONS]\n";
1633 cout << endl;
1634}
1635
1636void PrintHelp()
1637{
1638 Main::PrintHelp<StateMachineDrive<StateMachine,ConnectionDrive>>();
1639
1640 /* Additional help text which is printed after the configuration
1641 options goes here */
1642
1643 /*
1644 cout << "bla bla bla" << endl << endl;
1645 cout << endl;
1646 cout << "Environment:" << endl;
1647 cout << "environment" << endl;
1648 cout << endl;
1649 cout << "Examples:" << endl;
1650 cout << "test exam" << endl;
1651 cout << endl;
1652 cout << "Files:" << endl;
1653 cout << "files" << endl;
1654 cout << endl;
1655 */
1656}
1657
1658int main(int argc, const char* argv[])
1659{
1660 Configuration conf(argv[0]);
1661 conf.SetPrintUsage(PrintUsage);
1662 Main::SetupConfiguration(conf);
1663 SetupConfiguration(conf);
1664
1665 if (!conf.DoParse(argc, argv, PrintHelp))
1666 return 127;
1667
1668 //try
1669 {
1670 // No console access at all
1671 if (!conf.Has("console"))
1672 {
1673 if (conf.Get<bool>("no-dim"))
1674 return RunShell<LocalStream, StateMachine, ConnectionDrive>(conf);
1675 else
1676 return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(conf);
1677 }
1678 // Cosole access w/ and w/o Dim
1679 if (conf.Get<bool>("no-dim"))
1680 {
1681 if (conf.Get<int>("console")==0)
1682 return RunShell<LocalShell, StateMachine, ConnectionDrive>(conf);
1683 else
1684 return RunShell<LocalConsole, StateMachine, ConnectionDrive>(conf);
1685 }
1686 else
1687 {
1688 if (conf.Get<int>("console")==0)
1689 return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
1690 else
1691 return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
1692 }
1693 }
1694 /*catch (std::exception& e)
1695 {
1696 cerr << "Exception: " << e.what() << endl;
1697 return -1;
1698 }*/
1699
1700 return 0;
1701}
Note: See TracBrowser for help on using the repository browser.