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

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