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

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