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

Last change on this file since 14676 was 14676, checked in by tbretz, 12 years ago
Implemented interpreting and requesting wobble position from the databas and the resources.
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", "S:1;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>(60), "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>(120), "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.