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

Last change on this file since 18399 was 18388, checked in by tbretz, 10 years ago
Another try to solve the problems with the emergency parking. Note that it is only tried if possible during sun-rise and never again.
File size: 109.1 KB
Line 
1#include <boost/regex.hpp>
2#include <boost/algorithm/string.hpp>
3
4#ifdef HAVE_SQL
5#include "Database.h"
6#endif
7
8#include "FACT.h"
9#include "Dim.h"
10#include "Event.h"
11#include "Shell.h"
12#include "StateMachineDim.h"
13#include "StateMachineAsio.h"
14#include "Connection.h"
15#include "LocalControl.h"
16#include "Configuration.h"
17#include "Timers.h"
18#include "Console.h"
19
20#include "HeadersDrive.h"
21
22#include "pal.h"
23#include "externals/nova.h"
24
25namespace ba = boost::asio;
26namespace bs = boost::system;
27
28using namespace std;
29using namespace Drive;
30
31// ------------------------------------------------------------------------
32
33// The Nova classes are in degree. This is to be used in rad
34struct RaDec
35{
36 double ra; // [rad]
37 double dec; // [rad]
38 RaDec() : ra(0), dec(0) { }
39 RaDec(double _ra, double _dec) : ra(_ra), dec(_dec) { }
40};
41
42struct RaDecHa : RaDec
43{
44 double ha; // [rad]
45 RaDecHa() : ha(0) { }
46 RaDecHa(double _ra, double _dec, double _ha) : RaDec(_ra, _dec), ha(_ha) { }
47};
48
49struct Local
50{
51 double zd;
52 double az;
53
54 Local(double _zd=0, double _az=0) : zd(_zd), az(_az) { }
55};
56
57struct Velocity : Local
58{
59 Velocity(double _zd=0, double _az=0) : Local(_zd, _az) { }
60 Velocity operator/(double f) const { return Velocity(zd/f, az/f); }
61 Velocity operator*(double f) const { return Velocity(zd*f, az*f); }
62};
63
64struct Encoder : Local // [units: revolutions]
65{
66 Encoder(double _zd=0, double _az=0) : Local(_zd, _az) { }
67
68 Encoder &operator*=(double f) { zd*=f; az*=f; return *this; }
69 Encoder &operator-=(const Encoder &enc) { zd-=enc.zd; az-=enc.az; return *this; }
70 Encoder operator*(double f) const { return Encoder(zd*f, az*f); }
71 Velocity operator/(double t) const { return Velocity(zd/t, az/t); }
72 Encoder Abs() const { return Encoder(fabs(zd), fabs(az)); }
73};
74
75struct ZdAz : Local // [units: rad]
76{
77 ZdAz(double _zd=0, double _az=0) : Local(_zd, _az) { }
78 ZdAz operator*(const double &f) const { return ZdAz(zd*f, az*f); }
79};
80
81struct Acceleration : Local
82{
83 Acceleration(double _zd=0, double _az=0) : Local(_zd, _az) { }
84 bool operator>(const Acceleration &a) const
85 {
86 return zd>a.zd || az>a.az;
87 }
88};
89
90Encoder operator-(const Encoder &a, const Encoder &b)
91{
92 return Encoder(a.zd-b.zd, a.az-b.az);
93}
94Velocity operator-(const Encoder &a, const Velocity &b)
95{
96 return Velocity(a.zd-b.zd, a.az-b.az);
97}
98Velocity operator-(const Velocity &a, const Velocity &b)
99{
100 return Velocity(a.zd-b.zd, a.az-b.az);
101}
102Encoder operator/(const Encoder &a, const Encoder &b)
103{
104 return Encoder(a.zd/b.zd, a.az/b.az);
105}
106
107struct Weather
108{
109 float hum;
110 float temp;
111 float press;
112 Time time;
113};
114
115struct Source
116{
117 Source() : ra(0), dec(0), mag(0), offset(0)
118 {
119 angles[0] = -90;
120 angles[1] = 90;
121 }
122
123 string name;
124 double ra; // [h]
125 double dec; // [deg]
126 double mag;
127
128 double offset;
129 array<double, 2> angles;
130};
131
132enum Planets_t
133{
134 kENone = -1,
135 kESun = 0,
136 kEMercury = 1,
137 kEVenus = 2,
138 kEMoon = 3, // earth moon barycentre
139 kEMars = 4,
140 kEJupiter = 5,
141 kESaturn = 6,
142 kEUranus = 7,
143 kENeptune = 8,
144 kEPluto = 9,
145};
146
147// ------------------------------------------------------------------------
148
149struct PointingSetup
150{
151 Source source; // Informations about source to track [h/deg]
152 Planets_t planet; // Id of the planet if tracking a planet
153 double start; // Starting time of wobble observation [mjd]
154 double orbit_period; // Time for one revolution (0:off) [day]
155 double wobble_offset; // Distance of wobble position [rad]
156 double wobble_angle; // Starting phi angle of wobble observation [rad]
157
158 PointingSetup(Planets_t p=kENone) : planet(p), start(Time::none), orbit_period(0) { }
159};
160
161struct PointingData
162{
163 // Pointing direction of the opticl axis of the telescope
164 RaDec source; // Informations about source to track [rad/rad]
165 RaDec pointing; // Catalog coordinates (J2000, FK5) [rad/rad] pointing position
166 RaDecHa apparent; // Apparent position on the sky [rad/rad]
167 ZdAz sky; // Apparent position on the sky [rad/rad]
168 Encoder mount; // Encoder position corresponding to 'sky' [deg/deg]
169 double mjd;
170};
171
172class PointingModel
173{
174private:
175 double fIe; // [rad] Index Error in Elevation
176 double fIa; // [rad] Index Error in Azimuth
177 double fFlop; // [rad] Vertical Sag
178 double fNpae; // [rad] Az-El Nonperpendicularity
179 double fCa; // [rad] Left-Right Collimation Error
180 double fAn; // [rad] Azimuth Axis Misalignment (N-S, 1st order)
181 double fAw; // [rad] Azimuth Axis Misalignment (E-W, 1st order)
182 double fAn2; // [rad] Azimuth Axis Misalignment (N-S, 2nd order)
183 double fAw2; // [rad] Azimuth Axis Misalignment (E-W, 2nd order)
184 double fTf; // [rad] Tube fluxture (sin)
185 double fTx; // [rad] Tube fluxture (tan)
186 double fNrx; // [rad] Nasmyth rotator displacement, horizontal
187 double fNry; // [rad] Nasmyth rotator displacement, vertical
188 double fCrx; // [rad] Alt/Az Coude Displacement (N-S)
189 double fCry; // [rad] Alt/Az Coude Displacement (E-W)
190 double fEces; // [rad] Elevation Centering Error (sin)
191 double fAces; // [rad] Azimuth Centering Error (sin)
192 double fEcec; // [rad] Elevation Centering Error (cos)
193 double fAcec; // [rad] Azimuth Centering Error (cos)
194
195public:
196
197 void Load(const string &name)
198 {
199 /*
200 ! MMT 1987 July 8
201 ! T 36 7.3622 41.448 -0.0481
202 ! IA -37.5465 20.80602
203 ! IE -13.9180 1.25217
204 ! NPAE +7.0751 26.44763
205 ! CA -6.9149 32.05358
206 ! AN +0.5053 1.40956
207 ! AW -2.2016 1.37480
208 ! END
209 */
210
211 ifstream fin(name);
212 if (!fin)
213 throw runtime_error("Cannot open file "+name+": "+strerror(errno));
214
215 map<string,double> coeff;
216
217 string buf;
218 while (getline(fin, buf))
219 {
220 buf = Tools::Trim(buf);
221
222 vector<string> vec;
223 boost::split(vec, buf, boost::is_any_of(" "), boost::token_compress_on);
224 if (vec.size()<2)
225 continue;
226
227 coeff[vec[0]] = atof(vec[1].c_str()) * M_PI/180;
228 }
229
230 fIe = coeff["IE"]; // [rad] Index Error in Elevation
231 fIa = coeff["IA"]; // [rad] Index Error in Azimuth
232 fFlop = coeff["FLOP"]; // [rad] Vertical Sag
233 fNpae = coeff["NPAE"]; // [rad] Az-El Nonperpendicularity
234 fCa = coeff["CA"]; // [rad] Left-Right Collimation Error
235 fAn = coeff["AN"]; // [rad] Azimuth Axis Misalignment (N-S, 1st order)
236 fAw = coeff["AW"]; // [rad] Azimuth Axis Misalignment (E-W, 1st order)
237 fAn2 = coeff["AN2"]; // [rad] Azimuth Axis Misalignment (N-S, 2nd order)
238 fAw2 = coeff["AW2"]; // [rad] Azimuth Axis Misalignment (E-W, 2nd order)
239 fTf = coeff["TF"]; // [rad] Tube fluxture (sin)
240 fTx = coeff["TX"]; // [rad] Tube fluxture (tan)
241 fNrx = coeff["NRX"]; // [rad] Nasmyth rotator displacement, horizontal
242 fNry = coeff["NRY"]; // [rad] Nasmyth rotator displacement, vertical
243 fCrx = coeff["CRX"]; // [rad] Alt/Az Coude Displacement (N-S)
244 fCry = coeff["CRY"]; // [rad] Alt/Az Coude Displacement (E-W)
245 fEces = coeff["ECES"]; // [rad] Elevation Centering Error (sin)
246 fAces = coeff["ACES"]; // [rad] Azimuth Centering Error (sin)
247 fEcec = coeff["ECEC"]; // [rad] Elevation Centering Error (cos)
248 fAcec = coeff["ACEC"]; // [rad] Azimuth Centering Error (cos)
249 }
250
251 struct AltAz
252 {
253 double alt;
254 double az;
255
256 AltAz(double _alt, double _az) : alt(_alt), az(_az) { }
257 AltAz(const ZdAz &za) : alt(M_PI/2-za.zd), az(za.az) { }
258
259 AltAz &operator+=(const AltAz &aa) { alt += aa.alt; az+=aa.az; return *this; }
260 AltAz &operator-=(const AltAz &aa) { alt -= aa.alt; az-=aa.az; return *this; }
261 };
262
263 double Sign(double val, double alt) const
264 {
265 // Some pointing corrections are defined as Delta ZA, which
266 // is (P. Wallace) defined [0,90]deg while Alt is defined
267 // [0,180]deg
268 return (M_PI/2-alt < 0 ? -val : val);
269 }
270
271 Encoder SkyToMount(AltAz p)
272 {
273 const AltAz CRX(-fCrx*sin(p.az-p.alt), fCrx*cos(p.az-p.alt)/cos(p.alt));
274 const AltAz CRY(-fCry*cos(p.az-p.alt), -fCry*sin(p.az-p.alt)/cos(p.alt));
275 p += CRX;
276 p += CRY;
277
278 const AltAz NRX(fNrx*sin(p.alt), -fNrx);
279 const AltAz NRY(fNry*cos(p.alt), -fNry*tan(p.alt));
280 p += NRX;
281 p += NRY;
282
283 const AltAz CES(-fEces*sin(p.alt), -fAces*sin(p.az));
284 const AltAz CEC(-fEcec*cos(p.alt), -fAcec*cos(p.az));
285 p += CES;
286 p += CEC;
287
288 const AltAz TX(Sign(fTx/tan(p.alt), p.alt), 0);
289 const AltAz TF(Sign(fTf*cos(p.alt), p.alt), 0);
290 //p += TX;
291 p += TF;
292
293 const AltAz CA(0, -fCa/cos(p.alt));
294 p += CA;
295
296 const AltAz NPAE(0, -fNpae*tan(p.alt));
297 p += NPAE;
298
299 const AltAz AW2( fAw2*sin(p.az*2), -fAw2*cos(p.az*2)*tan(p.alt));
300 const AltAz AN2(-fAn2*cos(p.az*2), -fAn2*sin(p.az*2)*tan(p.alt));
301 const AltAz AW1( fAw *sin(p.az), -fAw *cos(p.az) *tan(p.alt));
302 const AltAz AN1(-fAn *cos(p.az), -fAn *sin(p.az) *tan(p.alt));
303 p += AW2;
304 p += AN2;
305 p += AW1;
306 p += AN1;
307
308 const AltAz FLOP(Sign(fFlop, p.alt), 0);
309 p += FLOP;
310
311 const AltAz I(fIe, fIa);
312 p += I;
313
314 return Encoder(90 - p.alt*180/M_PI, p.az *180/M_PI);
315 }
316
317 ZdAz MountToSky(const Encoder &mnt) const
318 {
319 AltAz p(M_PI/2-mnt.zd*M_PI/180, mnt.az*M_PI/180);
320
321 const AltAz I(fIe, fIa);
322 p -= I;
323
324 const AltAz FLOP(Sign(fFlop, p.alt), 0);
325 p -= FLOP;
326
327 const AltAz AW1( fAw *sin(p.az), -fAw *cos(p.az) *tan(p.alt));
328 const AltAz AN1(-fAn *cos(p.az), -fAn *sin(p.az) *tan(p.alt));
329 const AltAz AW2( fAw2*sin(p.az*2), -fAw2*cos(p.az*2)*tan(p.alt));
330 const AltAz AN2(-fAn2*cos(p.az*2), -fAn2*sin(p.az*2)*tan(p.alt));
331 p -= AW1;
332 p -= AN1;
333 p -= AW2;
334 p -= AN2;
335
336 const AltAz NPAE(0, -fNpae*tan(p.alt));
337 p -= NPAE;
338
339 const AltAz CA(0, -fCa/cos(p.alt));
340 p -= CA;
341
342 const AltAz TF(Sign(fTf*cos(p.alt), p.alt), 0);
343 const AltAz TX(Sign(fTx/tan(p.alt), p.alt), 0);
344 p -= TF;
345 //p -= TX;
346
347 const AltAz CEC(-fEcec*cos(p.alt), -fAcec*cos(p.az));
348 const AltAz CES(-fEces*sin(p.alt), -fAces*sin(p.az));
349 p -= CEC;
350 p -= CES;
351
352 const AltAz NRY(fNry*cos(p.alt), -fNry*tan(p.alt));
353 const AltAz NRX(fNrx*sin(p.alt), -fNrx);
354 p -= NRY;
355 p -= NRX;
356
357 const AltAz CRY(-fCry*cos(p.az-p.alt), -fCry*sin(p.az-p.alt)/cos(p.alt));
358 const AltAz CRX(-fCrx*sin(p.az-p.alt), fCrx*cos(p.az-p.alt)/cos(p.alt));
359 p -= CRY;
360 p -= CRX;
361
362 return ZdAz(M_PI/2-p.alt, p.az);
363 }
364
365 PointingData CalcPointingPos(const PointingSetup &setup, double _mjd, const Weather &weather, uint16_t timeout, bool tpoint=false)
366 {
367 PointingData out;
368 out.mjd = _mjd;
369
370 const double elong = Nova::ORM().lng * M_PI/180;
371 const double lat = Nova::ORM().lat * M_PI/180;
372 const double height = 2200;
373
374 const bool valid = weather.time+boost::posix_time::seconds(timeout) > Time();
375
376 const double temp = valid ? weather.temp : 10;
377 const double hum = valid ? weather.hum : 0.25;
378 const double press = valid ? weather.press : 780;
379
380 const double dtt = palDtt(_mjd); // 32.184 + 35
381
382 const double tdb = _mjd + dtt/3600/24;
383 const double dut = 0;
384
385 // prepare calculation: Mean Place to geocentric apperent
386 // (UTC would also do, except for the moon?)
387 double fAmprms[21];
388 palMappa(2000.0, tdb, fAmprms); // Epoche, TDB
389
390 // prepare: Apperent to observed place
391 double fAoprms[14];
392 palAoppa(_mjd, dut, // mjd, Delta UT=UT1-UTC
393 elong, lat, height, // long, lat, height
394 0, 0, // polar motion x, y-coordinate (radians)
395 273.155+temp, press, hum, // temp, pressure, humidity
396 0.40, 0.0065, // wavelength, tropo lapse rate
397 fAoprms);
398
399 out.source.ra = setup.source.ra * M_PI/ 12;
400 out.source.dec = setup.source.dec * M_PI/180;
401
402 if (setup.planet!=kENone)
403 {
404 // coordinates of planet: topocentric, equatorial, J2000
405 // One can use TT instead of TDB for all planets (except the moon?)
406 double ra, dec, diam;
407 palRdplan(tdb, setup.planet, elong, lat, &ra, &dec, &diam);
408
409 // ---- apparent to mean ----
410 palAmpqk(ra, dec, fAmprms, &out.source.ra, &out.source.dec);
411 }
412
413 if (setup.wobble_offset<=0 || tpoint)
414 {
415 out.pointing.dec = out.source.dec;
416 out.pointing.ra = out.source.ra;
417 }
418 else
419 {
420 const double dphi =
421 setup.orbit_period==0 ? 0 : 2*M_PI*(_mjd-setup.start)/setup.orbit_period;
422
423 const double phi = setup.wobble_angle + dphi;
424
425 const double cosdir = cos(phi);
426 const double sindir = sin(phi);
427 const double cosoff = cos(setup.wobble_offset);
428 const double sinoff = sin(setup.wobble_offset);
429 const double cosdec = cos(out.source.dec);
430 const double sindec = sin(out.source.dec);
431
432 const double sintheta = sindec*cosoff + cosdec*sinoff*cosdir;
433
434 const double costheta = sintheta>1 ? 0 : sqrt(1 - sintheta*sintheta);
435
436 const double cosdeltara = (cosoff - sindec*sintheta)/(cosdec*costheta);
437 const double sindeltara = sindir*sinoff/costheta;
438
439 out.pointing.dec = asin(sintheta);
440 out.pointing.ra = atan2(sindeltara, cosdeltara) + out.source.ra;
441 }
442
443 // ---- Mean to apparent ----
444 double r=0, d=0;
445 palMapqkz(out.pointing.ra, out.pointing.dec, fAmprms, &r, &d);
446
447 //
448 // Doesn't work - don't know why
449 //
450 // slaMapqk (radec.Ra(), radec.Dec(), rdpm.Ra(), rdpm.Dec(),
451 // 0, 0, (double*)fAmprms, &r, &d);
452 //
453
454 // -- apparent to observed --
455 palAopqk(r, d, fAoprms,
456 &out.sky.az, // observed azimuth (radians: N=0,E=90) [-pi, pi]
457 &out.sky.zd, // observed zenith distance (radians) [-pi/2, pi/2]
458 &out.apparent.ha, // observed hour angle (radians)
459 &out.apparent.dec, // observed declination (radians)
460 &out.apparent.ra); // observed right ascension (radians)
461
462 // ----- fix ambiguity -----
463 if (out.sky.zd<0)
464 {
465 out.sky.zd = -out.sky.zd;
466 out.sky.az += out.sky.az<0 ? M_PI : -M_PI;
467 }
468
469 // Star culminating behind zenith and Az between ~90 and ~180deg
470 if (out.source.dec<lat && out.sky.az>0)
471 out.sky.az -= 2*M_PI;
472
473 out.mount = SkyToMount(out.sky);
474
475 return out;
476 }
477};
478
479// ------------------------------------------------------------------------
480
481
482class ConnectionDrive : public Connection
483{
484 uint16_t fVerbosity;
485
486public:
487 virtual void UpdatePointing(const Time &, const array<double, 2> &)
488 {
489 }
490
491 virtual void UpdateTracking(const Time &, const array<double, 12> &)
492 {
493 }
494
495 virtual void UpdateStatus(const Time &, const array<uint8_t, 3> &)
496 {
497 }
498
499 virtual void UpdateTPoint(const Time &, const DimTPoint &, const string &)
500 {
501 }
502
503 virtual void UpdateSource(const Time &, const string &, bool)
504 {
505 }
506 virtual void UpdateSource(const Time &,const array<double, 5> &, const string& = "")
507 {
508 }
509
510private:
511 enum NodeId_t
512 {
513 kNodeAz = 1,
514 kNodeZd = 3
515 };
516
517 enum
518 {
519 kRxNodeguard = 0xe,
520 kRxPdo1 = 3,
521 kRxPdo2 = 5,
522 kRxPdo3 = 7,
523 kRxPdo4 = 9,
524 kRxSdo = 0xb,
525 kRxSdo4 = 0x40|0x3,
526 kRxSdo2 = 0x40|0xb,
527 kRxSdo1 = 0x40|0xf,
528 kRxSdoOk = 0x60,
529 kRxSdoErr = 0x80,
530
531 kTxSdo = 0x40,
532 kTxSdo4 = 0x20|0x3,
533 kTxSdo2 = 0x20|0xb,
534 kTxSdo1 = 0x20|0xf,
535 };
536
537 void SendCanFrame(uint16_t cobid,
538 uint8_t m0=0, uint8_t m1=0, uint8_t m2=0, uint8_t m3=0,
539 uint8_t m4=0, uint8_t m5=0, uint8_t m6=0, uint8_t m7=0)
540 {
541 const uint16_t desc = (cobid<<5) | 8;
542
543 vector<uint8_t> data(11);
544 data[0] = 10;
545 data[1] = desc>>8;
546 data[2] = desc&0xff;
547
548 const uint8_t msg[8] = { m0, m1, m2, m3, m4, m5, m6, m7 };
549 memcpy(data.data()+3, msg, 8);
550
551 PostMessage(data);
552 }
553
554 enum Index_t
555 {
556 kReqArmed = 0x1000,
557 kReqPDO = 0x1001,
558 kReqErrStat = 0x1003,
559 kReqSoftVer = 0x100a,
560 kReqKeepAlive = 0x100b,
561 kReqVel = 0x2002,
562 kReqVelRes = 0x6002,
563 kReqVelMax = 0x6003,
564 kReqPos = 0x6004,
565 kReqPosRes = 0x6501,
566
567 kSetArmed = 0x1000,
568 kSetPointVel = 0x2002,
569 kSetAcc = 0x2003,
570 kSetRpmMode = 0x3006,
571 kSetTrackVel = 0x3007,
572 kSetLedVoltage = 0x4000,
573 kSetPosition = 0x6004,
574 };
575
576 static uint32_t String(uint8_t b0=0, uint8_t b1=0, uint8_t b2=0, uint8_t b3=0)
577 {
578 return uint32_t(b0)<<24 | uint32_t(b1)<<16 | uint32_t(b2)<<8 | uint32_t(b3);
579 }
580
581 uint32_t fVelRes[2];
582 uint32_t fVelMax[2];
583 uint32_t fPosRes[2];
584
585 uint32_t fErrCode[2];
586
587 void HandleSdo(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
588 const uint32_t &val, const Time &tv)
589 {
590 if (fVerbosity>0)
591 {
592 ostringstream out;
593 out << hex;
594 out << "SDO[" << int(node) << "] " << idx << "/" << int(subidx) << ": " << val << dec;
595 Out() << out.str() << endl;
596 }
597
598 switch (idx)
599 {
600 case kReqArmed:
601 //fArmed = val==1;
602 return;
603
604 case kReqErrStat:
605 {
606 fErrCode[node/2] = (val>>8);
607 LogErrorCode(node);
608 }
609 return;
610
611 case kReqSoftVer:
612 //fSoftVersion = val;
613 return;
614
615 case kReqKeepAlive:
616 // Do not display, this is used for CheckConnection
617 fIsInitialized[node/2] = true;
618 return;
619
620 case kReqVel:
621 //fVel = val;
622 return;
623
624 case kReqPos:
625 switch (subidx)
626 {
627 case 0:
628 fPdoPos1[node/2] = val;
629 fPdoTime1[node/2] = tv;
630 fHasChangedPos1[node/2] = true;
631 return;
632 case 1:
633 fPdoPos2[node/2] = val;
634 fPdoTime2[node/2] = tv;
635 fHasChangedPos2[node/2] = true;
636 return;
637 }
638 break;
639
640 case kReqVelRes:
641 fVelRes[node/2] = val;
642 return;
643
644 case kReqVelMax:
645 fVelMax[node/2] = val;
646 return;
647
648 case kReqPosRes:
649 fPosRes[node/2] = val;
650 return;
651 }
652
653 ostringstream str;
654 str << "HandleSDO: Idx=0x"<< hex << idx << "/" << (int)subidx;
655 str << ", val=0x" << val;
656 Warn(str);
657 }
658
659 void HandleSdoOk(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
660 const Time &)
661 {
662 ostringstream out;
663 out << hex;
664 out << "SDO-OK[" << int(node) << "] " << idx << "/" << int(subidx) << dec << " ";
665
666 switch (idx)
667 {
668 case kSetArmed:
669 out << "(Armed state set)";
670 break;
671 /*
672 case 0x1001:
673 Out() << inf2 << "- " << GetNodeName() << ": PDOs requested." << endl;
674 return;
675 */
676 case kSetPointVel:
677 out << "(Pointing velocity set)";
678 break;
679
680 case kSetAcc:
681 out << "(Acceleration set)";
682 break;
683
684 case kSetRpmMode:
685 out << "(RPM mode set)";
686 break;
687
688 case kSetLedVoltage:
689 out << "(LED Voltage set)";
690 Info(out);
691 return;
692 /*
693 case 0x3007:
694 //Out() << inf2 << "- Velocity set (" << GetNodeName() << ")" << endl;
695 return;
696
697 case 0x4000:
698 HandleNodeguard(tv);
699 return;
700
701 case 0x6000:
702 Out() << inf2 << "- " << GetNodeName() << ": Rotation direction set." << endl;
703 return;
704
705 case 0x6002:
706 Out() << inf2 << "- " << GetNodeName() << ": Velocity resolution set." << endl;
707 return;
708 */
709 case kSetPosition:
710 out << "(Absolute positioning started)";
711 break;
712 /*
713 case 0x6005:
714 Out() << inf2 << "- " << GetNodeName() << ": Relative positioning started." << endl;
715 fPosActive = kTRUE; // Make sure that the status is set correctly already before the first PDO
716 return;*/
717 }
718 /*
719 Out() << warn << setfill('0') << "WARNING - Nodedrv::HandleSDOOK: ";
720 Out() << "Node #" << dec << (int)fId << ": Sdo=" << hex << idx << "/" << (int)subidx << " set.";
721 Out() << endl;
722 */
723
724 if (fVerbosity>1)
725 Out() << out.str() << endl;
726 }
727
728 void HandleSdoError(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
729 const Time &)
730 {
731 ostringstream out;
732 out << hex;
733 out << "SDO-ERR[" << int(node) << "] " << idx << "/" << int(subidx) << dec;
734 Out() << out.str() << endl;
735 }
736
737
738 int32_t fPdoPos1[2];
739 int32_t fPdoPos2[2];
740
741 Time fPdoTime1[2];
742public:
743 Time fPdoTime2[2];
744private:
745 bool fHasChangedPos1[2];
746 bool fHasChangedPos2[2];
747
748 void HandlePdo1(const uint8_t &node, const uint8_t *data, const Time &tv)
749 {
750 const uint32_t pos1 = (data[3]<<24) | (data[2]<<16) | (data[1]<<8) | data[0];
751 const uint32_t pos2 = (data[7]<<24) | (data[6]<<16) | (data[5]<<8) | data[4];
752
753 if (fVerbosity>2)
754 Out() << Time().GetAsStr("%M:%S.%f") << " PDO1[" << (int)node << "] " << 360.*int32_t(pos1)/fPosRes[node/2] << " " << 360.*int32_t(pos2)/fPosRes[node/2] << endl;
755
756 // Once every few milliseconds!
757
758 fPdoPos1[node/2] = pos1;
759 fPdoTime1[node/2] = tv;
760 fHasChangedPos1[node/2] = true;
761
762 fPdoPos2[node/2] = pos2;
763 fPdoTime2[node/2] = tv;
764 fHasChangedPos2[node/2] = true;
765 }
766
767 uint8_t fStatusAxis[2];
768 uint8_t fStatusSys;
769
770 enum {
771 kUpsAlarm = 0x01, // UPS Alarm (FACT only)
772 kUpsBattery = 0x02, // UPS on battery (FACT only)
773 kUpsCharging = 0x04, // UPS charging (FACT only)
774 kEmergencyOk = 0x10, // Emergency button released
775 kOvervoltOk = 0x20, // Overvoltage protection ok
776 kManualMode = 0x40, // Manual mode button pressed
777
778 kAxisBb = 0x01, // IndraDrive reports Bb (Regler betriebsbereit)
779 kAxisMoving = 0x02, // SPS reports
780 kAxisRpmMode = 0x04, // SPS reports
781 kAxisRf = 0x20, // IndraDrive reports Rf (Regler freigegeben)
782 kAxisHasPower = 0x80 // IndraDrive reports axis power on
783 };
784
785 //std::function<void(const Time &, const array<uint8_t, 3>&)> fUpdateStatus;
786
787 void HandlePdo3(const uint8_t &node, const uint8_t *data, const Time &tv)
788 {
789 /*
790 TX1M_STATUS.0 := 1;
791 TX1M_STATUS.1 := ((NOT X_in_Standstill OR NOT X_in_AntriebHalt) AND (NOT X_PC_VStart AND NOT X_in_Pos)) OR X_PC_AnnounceStartMovement;
792 TX1M_STATUS.2 := X_PC_VStart;
793 TX1M_STATUS.6 := NOT X_ist_freigegeben;
794
795 TX3M_STATUS.0 := X_ist_betriebsbereit;
796 TX3M_STATUS.1 := 1;
797 TX3M_STATUS.2 := Not_Aus_IO;
798 TX3M_STATUS.3 := UeberspannungsSchutz_OK;
799 TX3M_STATUS.4 := FB_soll_drehen_links OR FB_soll_drehen_rechts OR FB_soll_schwenk_auf OR FB_soll_schwenk_ab;
800 TX3M_STATUS.5 := X_ist_freigegeben;
801 TX3M_STATUS.6 := 1;
802 TX3M_STATUS.7 := LeistungEinAz;
803
804 TX3M_STATUS.8 := NOT UPS_ALARM;
805 TX3M_STATUS.9 := UPS_BattMode;
806 TX3M_STATUS.10 := UPS_Charging;
807 */
808
809 const uint8_t sys = ((data[0] & 0x1c)<<2) | (data[1]);
810 if (fStatusSys!=sys)
811 {
812 fStatusSys = sys;
813
814 const bool alarm = sys&kUpsAlarm; // 01 TX3M.8 100
815 const bool batt = sys&kUpsBattery; // 02 TX3M.9 200
816 const bool charge = sys&kUpsCharging; // 04 TX3M.10 400
817 const bool emcy = sys&kEmergencyOk; // 10 TX3M.2 04
818 const bool vltg = sys&kOvervoltOk; // 20 TX3M.3 08
819 const bool mode = sys&kManualMode; // 40 TX3M.4 10
820
821 ostringstream out;
822 if (alarm) out << " UPS-PowerLoss";
823 if (batt) out << " UPS-OnBattery";
824 if (charge) out << " UPS-Charging";
825 if (emcy) out << " EmcyOk";
826 if (vltg) out << " OvervoltOk";
827 if (mode) out << " ManualMove";
828
829 Info("New system status["+to_string(node)+"]:"+out.str());
830 if (fVerbosity>1)
831 Out() << "PDO3[" << (int)node << "] StatusSys=" << hex << (int)fStatusSys << dec << endl;
832 }
833
834 const uint8_t axis = (data[0]&0xa1) | (data[3]&0x06);
835 if (fStatusAxis[node/2]!=axis)
836 {
837 fStatusAxis[node/2] = axis;
838
839 const bool ready = axis&kAxisBb; // 01
840 const bool move = axis&kAxisMoving; // 02
841 const bool rpm = axis&kAxisRpmMode; // 04
842 const bool rf = axis&kAxisRf; // 20
843 const bool power = axis&kAxisHasPower; // 80
844
845 ostringstream out;
846 if (ready) out << " DKC-Ready";
847 if (move) out << " Moving";
848 if (rpm) out << " RpmMode";
849 if (rf) out << " RF";
850 if (power) out << " PowerOn";
851
852 Info("New axis status["+to_string(node)+"]:"+out.str());
853 if (fVerbosity>1)
854 Out() << "PDO3[" << (int)node << "] StatusAxis=" << hex << (int)fStatusAxis[node/2] << dec << endl;
855 }
856
857 array<uint8_t, 3> arr = {{ fStatusAxis[0], fStatusAxis[1], fStatusSys }};
858 UpdateStatus(tv, arr);
859 }
860
861 string ErrCodeToString(uint32_t code) const
862 {
863 switch (code)
864 {
865 case 0: return "offline";
866 case 0xa000: case 0xa0000:
867 case 0xa001: case 0xa0001:
868 case 0xa002: case 0xa0002:
869 case 0xa003: case 0xa0003: return "Communication phase "+to_string(code&0xf);
870 case 0xa010: case 0xa0010: return "Drive HALT";
871 case 0xa012: case 0xa0012: return "Control and power section ready for operation";
872 case 0xa013: case 0xa0013: return "Ready for power on";
873 case 0xa100: case 0xa0100: return "Drive in Torque mode";
874 case 0xa101: case 0xa0101: return "Drive in Velocity mode";
875 case 0xa102: case 0xa0102: return "Position control mode with encoder 1";
876 case 0xa103: case 0xa0103: return "Position control mode with encoder 2";
877 case 0xa104: case 0xa0104: return "Position control mode with encoder 1, lagless";
878 case 0xa105: case 0xa0105: return "Position control mode with encoder 2, lagless";
879 case 0xa106: case 0xa0106: return "Drive controlled interpolated positioning with encoder 1";
880 case 0xa107: case 0xa0107: return "Drive controlled interpolated positioning with encoder 2";
881 case 0xa108: case 0xa0108: return "Drive controlled interpolated positioning with encoder 1, lagless";
882 case 0xa109: case 0xa0109: return "Drive controlled interpolated positioning with encoder 2, lagless";
883 //case 0xa146: return "Drive controlled interpolated relative positioning with encoder 1";
884 //case 0xa147: return "Drive controlled interpolated relative positioning with encoder 2";
885 //case 0xa148: return "Drive controlled interpolated relative positioning lagless with encoder 1";
886 //case 0xa149: return "Drive controlled interpolated relative positioning lagless with encoder 2";
887 case 0xa150: case 0xa0150: return "Drive controlled positioning with encoder 1";
888 case 0xa151: case 0xa0151: return "Drive controlled positioning with encoder 1, lagless";
889 case 0xa152: case 0xa0152: return "Drive controlled positioning with encoder 2";
890 case 0xa153: case 0xa0153: return "Drive controlled positioning with encoder 2, lagless";
891 case 0xa208: return "Jog mode positive";
892 case 0xa218: return "Jog mode negative";
893 case 0xa400: case 0xa4000: return "Automatic drive check and adjustment";
894 case 0xa401: case 0xa4001: return "Drive decelerating to standstill";
895 case 0xa800: case 0xa0800: return "Unknown operation mode";
896 case 0xc217: return "Motor encoder reading error";
897 case 0xc218: return "Shaft encoder reading error";
898 case 0xc220: return "Motor encoder initialization error";
899 case 0xc221: return "Shaft encoder initialization error";
900 case 0xc300: return "Command: set absolute measure";
901 case 0xc400: case 0xc0400: return "Switching to parameter mode";
902 case 0xc401: case 0xc0401: return "Drive active, switching mode not allowed";
903 case 0xc500: case 0xc0500: return "Error reset";
904 case 0xc600: case 0xc0600: return "Drive controlled homing procedure";
905 case 0xe225: return "Motor overload";
906 case 0xe249: case 0xe2049: return "Positioning command velocity exceeds limit bipolar";
907 case 0xe250: return "Drive overtemp warning";
908 case 0xe251: return "Motor overtemp warning";
909 case 0xe252: return "Bleeder overtemp warning";
910 case 0xe257: return "Continous current limit active";
911 case 0xe2819: return "Main power failure";
912 case 0xe259: return "Command velocity limit active";
913 case 0xe8260: return "Torque limit active";
914 case 0xe264: return "Target position out of numerical range";
915 case 0xe829: case 0xe8029: return "Positive position limit exceeded";
916 case 0xe830: case 0xe8030: return "Negative position limit exceeded";
917 case 0xe831: return "Position limit reached during jog";
918 case 0xe834: return "Emergency-Stop";
919 case 0xe842: return "Both end-switches activated";
920 case 0xe843: return "Positive end-switch activated";
921 case 0xe844: return "Negative end-switch activated";
922 case 0xf218: case 0xf2018: return "Amplifier overtemp shutdown";
923 case 0xf219: case 0xf2019: return "Motor overtemp shutdown";
924 case 0xf220: return "Bleeder overload shutdown";
925 case 0xf221: case 0xf2021: return "Motor temperature surveillance defective";
926 case 0xf2022: return "Unit temperature surveillance defective";
927 case 0xf224: return "Maximum breaking time exceeded";
928 case 0xf2025: return "Drive not ready for power on";
929 case 0xf228: case 0xf2028: return "Excessive control deviation";
930 case 0xf250: return "Overflow of target position preset memory";
931 case 0xf257: case 0xf2057: return "Command position out of range";
932 case 0xf269: return "Error during release of the motor holding brake";
933 case 0xf276: return "Absolute encoder out of allowed window";
934 case 0xf2174: return "Lost reference of motor encoder";
935 case 0xf409: case 0xf4009: return "Bus error on Profibus interface";
936 case 0xf434: return "Emergency-Stop";
937 case 0xf629: return "Positive position limit exceeded";
938 case 0xf630: return "Negative position limit exceeded";
939 case 0xf634: return "Emergency-Stop";
940 case 0xf643: return "Positive end-switch activated";
941 case 0xf644: return "Negative end-switch activated";
942 case 0xf8069: return "15V DC error";
943 case 0xf870: case 0xf8070: return "24V DC error";
944 case 0xf878: case 0xf8078: return "Velocity loop error";
945 case 0xf8079: return "Velocity limit exceeded";
946 case 0xf2026: return "Undervoltage in power section";
947 }
948 return "unknown";
949 }
950
951 void LogErrorCode(uint32_t node)
952 {
953 const uint8_t typ = fErrCode[node/2]>>16;
954
955 ostringstream out;
956 out << "IndraDrive ";
957 out << (node==1?"Az":"Zd");
958 out << " [" << hex << fErrCode[node/2];
959 out << "]: ";
960 out << ErrCodeToString(fErrCode[node/2]);
961 out << (typ==0xf || typ==0xe ? "!" : ".");
962
963 switch (typ)
964 {
965 case 0xf: Error(out); break;
966 case 0xe: Warn(out); break;
967 case 0xa: Info(out); break;
968 case 0x0:
969 case 0xc:
970 case 0xd: Message(out); break;
971 default: Fatal(out); break;
972 }
973 }
974
975 void HandlePdo2(const uint8_t &node, const uint8_t *data, const Time &)
976 {
977 fErrCode[node/2] = (data[4]<<24) | (data[5]<<16) | (data[6]<<8) | data[7];
978
979 if (fVerbosity>0)
980 Out() << "PDO2[" << int(node) << "] err=" << hex << fErrCode[node/2] << endl;
981
982 LogErrorCode(node);
983 }
984
985 struct SDO
986 {
987 uint8_t node;
988 uint8_t req;
989 uint16_t idx;
990 uint8_t subidx;
991 uint32_t val;
992
993 SDO(uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v=0)
994 : node(n), req(r&0xf), idx(i), subidx(s), val(v) { }
995
996 bool operator==(const SDO &s) const
997 {
998 return node==s.node && idx==s.idx && subidx==s.subidx;
999 }
1000 };
1001
1002 struct Timeout_t : SDO, ba::deadline_timer
1003 {
1004
1005 Timeout_t(ba::io_service& ioservice,
1006 uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v, uint16_t millisec) : SDO(n, r, i, s, v),
1007 ba::deadline_timer(ioservice)
1008 {
1009 expires_from_now(boost::posix_time::milliseconds(millisec));
1010 }
1011 // get_io_service()
1012 };
1013
1014 std::list<Timeout_t> fTimeouts;
1015
1016 vector<uint8_t> fData;
1017
1018 void HandleReceivedData(const boost::system::error_code& err, size_t bytes_received, int)
1019 {
1020 // Do not schedule a new read if the connection failed.
1021 if (bytes_received!=11 || fData[0]!=10 || err)
1022 {
1023 if (err==ba::error::eof)
1024 Warn("Connection closed by remote host (cosy).");
1025
1026 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
1027 // 125: Operation canceled
1028 if (err && err!=ba::error::eof && // Connection closed by remote host
1029 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
1030 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
1031 {
1032 ostringstream str;
1033 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
1034 Error(str);
1035 }
1036 PostClose(err!=ba::error::basic_errors::operation_aborted);
1037 return;
1038 }
1039
1040 Time now;
1041
1042 const uint16_t desc = fData[1]<<8 | fData[2];
1043 const uint16_t cobid = desc>>5;
1044
1045 const uint8_t *data = fData.data()+3;
1046
1047 const uint16_t fcode = cobid >> 7;
1048 const uint8_t node = cobid & 0x1f;
1049
1050 switch (fcode)
1051 {
1052 case kRxNodeguard:
1053 Out() << "Received nodeguard" << endl;
1054 //HandleNodeguard(node, now);
1055 break;
1056
1057 case kRxSdo:
1058 {
1059 const uint8_t cmd = data[0];
1060 const uint16_t idx = data[1] | (data[2]<<8);
1061 const uint8_t subidx = data[3];
1062 const uint32_t dat = data[4] | (data[5]<<8) | (data[6]<<16) | (data[7]<<24);
1063
1064 const auto it = find(fTimeouts.begin(), fTimeouts.end(), SDO(node, cmd, idx, subidx));
1065 if (it!=fTimeouts.end())
1066 {
1067 // This will call the handler and in turn remove the object from the list
1068 it->cancel();
1069 }
1070
1071 switch (cmd)
1072 {
1073 case kRxSdo4: // answer to 0x40 with 4 bytes of data
1074 HandleSdo(node, idx, subidx, dat, now);
1075 break;
1076
1077 case kRxSdo2: // answer to 0x40 with 2 bytes of data
1078 HandleSdo(node, idx, subidx, dat&0xffff, now);
1079 break;
1080
1081 case kRxSdo1: // answer to 0x40 with 1 byte of data
1082 HandleSdo(node, idx, subidx, dat&0xff, now);
1083 break;
1084
1085 case kRxSdoOk: // answer to a SDO_TX message
1086 HandleSdoOk(node, idx, subidx, now);
1087 break;
1088
1089 case kRxSdoErr: // error message
1090 HandleSdoError(node, idx, subidx, now);
1091 break;
1092
1093 default:
1094 {
1095 ostringstream out;
1096 out << "Invalid SDO command code " << hex << cmd << " received.";
1097 Error(out);
1098 PostClose(false);
1099 return;
1100 }
1101 }
1102 }
1103 break;
1104
1105 case kRxPdo1:
1106 HandlePdo1(node, data, now);
1107 break;
1108
1109 case kRxPdo2:
1110 HandlePdo2(node, data, now);
1111 break;
1112
1113 case kRxPdo3:
1114 HandlePdo3(node, data, now);
1115 break;
1116
1117 default:
1118 {
1119 ostringstream out;
1120 out << "Invalid function code " << hex << fcode << " received.";
1121 Error(out);
1122 PostClose(false);
1123 return;
1124 }
1125 }
1126
1127 StartReadReport();
1128 }
1129
1130 void StartReadReport()
1131 {
1132 ba::async_read(*this, ba::buffer(fData),
1133 boost::bind(&ConnectionDrive::HandleReceivedData, this,
1134 ba::placeholders::error, ba::placeholders::bytes_transferred, 0));
1135
1136 //AsyncWait(fInTimeout, 35000, &Connection::HandleReadTimeout); // 30s
1137 }
1138
1139 bool fIsInitialized[2];
1140
1141 // This is called when a connection was established
1142 void ConnectionEstablished()
1143 {
1144 //Info("Connection to PLC established.");
1145
1146 fIsInitialized[0] = false;
1147 fIsInitialized[1] = false;
1148
1149 SendSdo(kNodeZd, kSetArmed, 1);
1150 SendSdo(kNodeAz, kSetArmed, 1);
1151
1152 RequestSdo(kNodeZd, kReqErrStat);
1153 RequestSdo(kNodeAz, kReqErrStat);
1154
1155 SetRpmMode(false);
1156
1157 RequestSdo(kNodeZd, kReqPosRes);
1158 RequestSdo(kNodeAz, kReqPosRes);
1159
1160 RequestSdo(kNodeZd, kReqVelRes);
1161 RequestSdo(kNodeAz, kReqVelRes);
1162
1163 RequestSdo(kNodeZd, kReqVelMax);
1164 RequestSdo(kNodeAz, kReqVelMax);
1165
1166 RequestSdo(kNodeZd, kReqPos, 0);
1167 RequestSdo(kNodeAz, kReqPos, 0);
1168 RequestSdo(kNodeZd, kReqPos, 1);
1169 RequestSdo(kNodeAz, kReqPos, 1);
1170
1171 RequestSdo(kNodeZd, kReqKeepAlive);
1172 RequestSdo(kNodeAz, kReqKeepAlive);
1173
1174 StartReadReport();
1175 }
1176
1177 void HandleTimeoutImp(const std::list<Timeout_t>::iterator &ref, const bs::error_code &error)
1178 {
1179 if (error==ba::error::basic_errors::operation_aborted)
1180 return;
1181
1182 if (error)
1183 {
1184 ostringstream str;
1185 str << "SDO timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
1186 Error(str);
1187
1188 //PostClose();
1189 return;
1190 }
1191
1192 if (!is_open())
1193 {
1194 // For example: Here we could schedule a new accept if we
1195 // would not want to allow two connections at the same time.
1196 return;
1197 }
1198
1199 // Check whether the deadline has passed. We compare the deadline
1200 // against the current time since a new asynchronous operation
1201 // may have moved the deadline before this actor had a chance
1202 // to run.
1203 if (ref->expires_at() > ba::deadline_timer::traits_type::now())
1204 return;
1205
1206 ostringstream str;
1207 str << hex;
1208 str << "SDO timeout (";
1209 str << uint32_t(ref->node) << ": ";
1210 str << (ref->req==kTxSdo?"RX ":"TX ");
1211 str << ref->idx << "/" << uint32_t(ref->subidx) << " [" << ref->val << "] ";
1212 str << to_simple_string(ref->expires_from_now());
1213 str << ")";
1214
1215 Warn(str);
1216
1217 //PostClose();
1218 }
1219
1220 void HandleTimeout(const std::list<Timeout_t>::iterator &ref, const bs::error_code &error)
1221 {
1222 HandleTimeoutImp(ref, error);
1223 fTimeouts.erase(ref);
1224 }
1225
1226 void SendSdoRequest(uint8_t node, uint8_t req,
1227 uint16_t idx, uint8_t subidx, uint32_t val=0)
1228 {
1229 if (fVerbosity>1)
1230 Out() << "SDO-" << (req==kTxSdo?"REQ":"SET") << "[" << int(node) << "] " << idx << "/" << int(subidx) << " = " << val << endl;
1231
1232
1233 SendCanFrame(0x600|(node&0x1f), req, idx&0xff, idx>>8, subidx,
1234 val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff);
1235
1236 // - The boost::asio::basic_deadline_timer::expires_from_now()
1237 // function cancels any pending asynchronous waits, and returns
1238 // the number of asynchronous waits that were cancelled. If it
1239 // returns 0 then you were too late and the wait handler has
1240 // already been executed, or will soon be executed. If it
1241 // returns 1 then the wait handler was successfully cancelled.
1242 // - If a wait handler is cancelled, the bs::error_code passed to
1243 // it contains the value bs::error::operation_aborted.
1244
1245 const uint32_t milliseconds = 3000;
1246 fTimeouts.emplace_front(get_io_service(), node, req, idx, subidx, val, milliseconds);
1247
1248 const std::list<Timeout_t>::iterator &timeout = fTimeouts.begin();
1249
1250 timeout->async_wait(boost::bind(&ConnectionDrive::HandleTimeout, this, timeout, ba::placeholders::error));
1251 }
1252
1253public:
1254 ConnectionDrive(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
1255 fVerbosity(0), fData(11)
1256 {
1257 SetLogStream(&imp);
1258 }
1259
1260 void SetVerbosity(const uint16_t &v)
1261 {
1262 fVerbosity = v;
1263 }
1264
1265 uint16_t GetVerbosity() const
1266 {
1267 return fVerbosity;
1268 }
1269
1270 void RequestSdo(uint8_t node, uint16_t idx, uint8_t subidx=0)
1271 {
1272 SendSdoRequest(node, kTxSdo, idx, subidx);
1273 }
1274 void SendSdo(uint8_t node, uint16_t idx, uint8_t subidx, uint32_t val)
1275 {
1276 SendSdoRequest(node, kTxSdo4, idx, subidx, val);
1277 }
1278
1279 void SendSdo(uint8_t node, uint16_t idx, uint32_t val)
1280 {
1281 SendSdo(node, idx, 0, val);
1282 }
1283
1284 bool IsMoving() const
1285 {
1286 return (fStatusAxis[0]&kAxisMoving) || (fStatusAxis[1]&kAxisMoving)
1287 || (fStatusAxis[0]&kAxisRpmMode) || (fStatusAxis[1]&kAxisRpmMode);
1288 }
1289
1290 bool IsInitialized() const
1291 {
1292 // All important information has been successfully requested from the
1293 // SPS and the power control units are in RF (Regler freigegeben)
1294 return fIsInitialized[0] && fIsInitialized[1];
1295 }
1296
1297 bool HasError() const
1298 {
1299 const uint8_t typ0 = fErrCode[0]>>16;
1300 const uint8_t typ1 = fErrCode[1]>>16;
1301 return typ0==0xe || typ0==0xf || typ1==0xe || typ1==0xf;
1302 }
1303
1304 bool IsOnline() const
1305 {
1306 return fErrCode[0]!=0 && fErrCode[1]!=0;
1307 }
1308
1309 bool IsReady() const
1310 {
1311 return fStatusAxis[0]&kAxisRf && fStatusAxis[1]&kAxisRf;
1312 }
1313
1314 bool IsBlocked() const
1315 {
1316 return (fStatusSys&kEmergencyOk)==0 || (fStatusSys&kManualMode);
1317 }
1318
1319 Encoder GetSePos() const // [rev]
1320 {
1321 return Encoder(double(fPdoPos2[1])/fPosRes[1], double(fPdoPos2[0])/fPosRes[0]);
1322 }
1323
1324 double GetSeTime() const // [rev]
1325 {
1326 // The maximum difference here should not be larger than 100ms.
1327 // So th error we make on both axes should not exceed 50ms;
1328 return (Time(fPdoTime2[0]).Mjd()+Time(fPdoTime2[1]).Mjd())/2;
1329 }
1330
1331 Encoder GetVelUnit() const
1332 {
1333 return Encoder(fVelMax[1], fVelMax[0]);
1334 }
1335
1336 void SetRpmMode(bool mode)
1337 {
1338 const uint32_t val = mode ? String('s','t','r','t') : String('s','t','o','p');
1339 SendSdo(kNodeAz, kSetRpmMode, val);
1340 SendSdo(kNodeZd, kSetRpmMode, val);
1341 }
1342
1343 void SetAcceleration(const Acceleration &acc)
1344 {
1345 SendSdo(kNodeAz, kSetAcc, lrint(acc.az*1000000000+0.5));
1346 SendSdo(kNodeZd, kSetAcc, lrint(acc.zd*1000000000+0.5));
1347 }
1348
1349 void SetPointingVelocity(const Velocity &vel, double scale=1)
1350 {
1351 SendSdo(kNodeAz, kSetPointVel, lrint(vel.az*fVelMax[0]*scale));
1352 SendSdo(kNodeZd, kSetPointVel, lrint(vel.zd*fVelMax[1]*scale));
1353 }
1354 void SetTrackingVelocity(const Velocity &vel)
1355 {
1356 SendSdo(kNodeAz, kSetTrackVel, lrint(vel.az*fVelRes[0]));
1357 SendSdo(kNodeZd, kSetTrackVel, lrint(vel.zd*fVelRes[1]));
1358 }
1359
1360 void StartAbsolutePositioning(const Encoder &enc, bool zd, bool az)
1361 {
1362 if (az) SendSdo(kNodeAz, kSetPosition, lrint(enc.az*fPosRes[0]));
1363 if (zd) SendSdo(kNodeZd, kSetPosition, lrint(enc.zd*fPosRes[1]));
1364
1365 // Make sure that the status is set correctly already before the first PDO
1366 if (az) fStatusAxis[0] |= 0x02;
1367 if (zd) fStatusAxis[1] |= 0x02;
1368
1369 // FIXME: UpdateDim?
1370 }
1371
1372 void SetLedVoltage(uint32_t v1, uint32_t v2)
1373 {
1374 SendSdo(kNodeAz, 0x4000, v1);
1375 SendSdo(kNodeZd, 0x4000, v2);
1376 }
1377};
1378
1379
1380// ------------------------------------------------------------------------
1381
1382#include "DimDescriptionService.h"
1383
1384class ConnectionDimDrive : public ConnectionDrive
1385{
1386private:
1387 DimDescribedService fDimPointing;
1388 DimDescribedService fDimTracking;
1389 DimDescribedService fDimSource;
1390 DimDescribedService fDimTPoint;
1391 DimDescribedService fDimStatus;
1392
1393 // Update dim from a different thread to ensure that these
1394 // updates cannot block the main eventloop which eventually
1395 // also checks the timeouts
1396 Queue<pair<Time,array<double, 2>>> fQueuePointing;
1397 Queue<pair<Time,array<double, 12>>> fQueueTracking;
1398 Queue<tuple<Time,vector<char>,bool>> fQueueSource;
1399 Queue<pair<Time,vector<char>>> fQueueTPoint;
1400 Queue<pair<Time,array<uint8_t, 3>>> fQueueStatus;
1401
1402 bool SendPointing(const pair<Time,array<double,2>> &p)
1403 {
1404 fDimPointing.setData(p.second);
1405 fDimPointing.Update(p.first);
1406 return true;
1407 }
1408
1409 bool SendTracking(const pair<Time,array<double, 12>> &p)
1410 {
1411 fDimTracking.setData(p.second);
1412 fDimTracking.Update(p.first);
1413 return true;
1414 }
1415
1416 bool SendSource(const tuple<Time,vector<char>,bool> &t)
1417 {
1418 const Time &time = get<0>(t);
1419 const vector<char> &data = get<1>(t);
1420 const bool &tracking = get<2>(t);
1421
1422 fDimSource.setQuality(tracking);
1423 fDimSource.setData(data);
1424 fDimSource.Update(time);
1425 return true;
1426 }
1427
1428 bool SendStatus(const pair<Time,array<uint8_t, 3>> &p)
1429 {
1430 fDimStatus.setData(p.second);
1431 fDimStatus.Update(p.first);
1432 return true;
1433 }
1434
1435 bool SendTPoint(const pair<Time,vector<char>> &p)
1436 {
1437 fDimTPoint.setData(p.second);
1438 fDimTPoint.Update(p.first);
1439 return true;
1440 }
1441
1442public:
1443 void UpdatePointing(const Time &t, const array<double, 2> &arr)
1444 {
1445 fQueuePointing.emplace(t, arr);
1446 }
1447
1448 void UpdateTracking(const Time &t,const array<double, 12> &arr)
1449 {
1450 fQueueTracking.emplace(t, arr);
1451 }
1452
1453 void UpdateStatus(const Time &t, const array<uint8_t, 3> &arr)
1454 {
1455 fQueueStatus.emplace(t, arr);
1456 }
1457
1458 void UpdateTPoint(const Time &t, const DimTPoint &data,
1459 const string &name)
1460 {
1461 vector<char> dim(sizeof(data)+name.length()+1);
1462 memcpy(dim.data(), &data, sizeof(data));
1463 memcpy(dim.data()+sizeof(data), name.c_str(), name.length()+1);
1464
1465 fQueueTPoint.emplace(t, dim);
1466 }
1467
1468 void UpdateSource(const Time &t, const string &name, bool tracking)
1469 {
1470 vector<char> dat(5*sizeof(double)+31, 0);
1471 strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1472
1473 fQueueSource.emplace(t, dat, tracking);
1474 }
1475
1476 void UpdateSource(const Time &t, const array<double, 5> &arr, const string &name="")
1477 {
1478 vector<char> dat(5*sizeof(double)+31, 0);
1479 memcpy(dat.data(), arr.data(), 5*sizeof(double));
1480 strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1481
1482 fQueueSource.emplace(t, dat, true);
1483 }
1484
1485public:
1486 ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) :
1487 ConnectionDrive(ioservice, imp),
1488 fDimPointing("DRIVE_CONTROL/POINTING_POSITION", "D:1;D:1",
1489 "|Zd[deg]:Zenith distance (derived from encoder readout)"
1490 "|Az[deg]:Azimuth angle (derived from encoder readout)"),
1491 fDimTracking("DRIVE_CONTROL/TRACKING_POSITION", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1",
1492 "|Ra[h]:Command right ascension pointing direction (J2000)"
1493 "|Dec[deg]:Command declination pointing direction (J2000)"
1494 "|Ha[h]:Hour angle pointing direction"
1495 "|SrcRa[h]:Right ascension source (J2000)"
1496 "|SrcDec[deg]:Declination source (J2000)"
1497 "|SrcHa[h]:Hour angle source"
1498 "|Zd[deg]:Nominal zenith distance"
1499 "|Az[deg]:Nominal azimuth angle"
1500 "|dZd[deg]:Control deviation Zd"
1501 "|dAz[deg]:Control deviation Az"
1502 "|dev[arcsec]:Absolute control deviation"
1503 "|avgdev[arcsec]:Average control deviation used to define OnTrack"),
1504 fDimSource("DRIVE_CONTROL/SOURCE_POSITION", "D:1;D:1;D:1;D:1;D:1;C:31",
1505 "|Ra_src[h]:Source right ascension"
1506 "|Dec_src[deg]:Source declination"
1507 "|Offset[deg]:Wobble offset"
1508 "|Angle[deg]:Wobble angle"
1509 "|Period[min]:Time for one orbit"
1510 "|Name[string]:Source name if available"),
1511 fDimTPoint("DRIVE_CONTROL/TPOINT_DATA", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;S:1;S:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;C",
1512 "|Ra[h]:Command right ascension"
1513 "|Dec[deg]:Command declination"
1514 "|Zd_nom[deg]:Nominal zenith distance"
1515 "|Az_nom[deg]:Nominal azimuth angle"
1516 "|Zd_cur[deg]:Current zenith distance (calculated from image)"
1517 "|Az_cur[deg]:Current azimuth angle (calculated from image)"
1518 "|Zd_enc[deg]:Feedback zenith axis (from encoder)"
1519 "|Az_enc[deg]:Feedback azimuth angle (from encoder)"
1520 "|N_leds[cnt]:Number of detected LEDs"
1521 "|N_rings[cnt]:Number of rings used to calculate the camera center"
1522 "|Xc[pix]:X position of center in CCD camera frame"
1523 "|Yc[pix]:Y position of center in CCD camera frame"
1524 "|Ic[au]:Average intensity (LED intensity weighted with their frequency of occurance in the calculation)"
1525 "|Xs[pix]:X position of start in CCD camera frame"
1526 "|Ys[pix]:Y position of star in CCD camera frame"
1527 "|Ms[mag]:Artifical magnitude of star (calculated from image)"
1528 "|Phi[deg]:Rotation angle of image derived from detected LEDs"
1529 "|Mc[mag]:Catalog magnitude of star"
1530 "|Dx[arcsec]:De-rotated dx"
1531 "|Dy[arcsec]:De-rotated dy"
1532 "|Name[string]:Name of star"),
1533 fDimStatus("DRIVE_CONTROL/STATUS", "C:2;C:1", ""),
1534 fQueuePointing(std::bind(&ConnectionDimDrive::SendPointing, this, placeholders::_1)),
1535 fQueueTracking(std::bind(&ConnectionDimDrive::SendTracking, this, placeholders::_1)),
1536 fQueueSource( std::bind(&ConnectionDimDrive::SendSource, this, placeholders::_1)),
1537 fQueueTPoint( std::bind(&ConnectionDimDrive::SendTPoint, this, placeholders::_1)),
1538 fQueueStatus( std::bind(&ConnectionDimDrive::SendStatus, this, placeholders::_1))
1539 {
1540 }
1541
1542 // 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
1543};
1544
1545// ------------------------------------------------------------------------
1546
1547template <class T, class S>
1548class StateMachineDrive : public StateMachineAsio<T>
1549{
1550private:
1551 S fDrive;
1552
1553 ba::deadline_timer fTrackingLoop;
1554
1555 string fDatabase;
1556
1557 typedef map<string, Source> sources;
1558 sources fSources;
1559
1560 Weather fWeather;
1561 uint16_t fWeatherTimeout;
1562
1563 ZdAz fParkingPos;
1564
1565 PointingModel fPointingModel;
1566 PointingSetup fPointingSetup;
1567 Encoder fMovementTarget;
1568
1569 Time fSunRise;
1570
1571 Encoder fPointingMin;
1572 Encoder fPointingMax;
1573
1574 uint16_t fDeviationLimit;
1575 uint16_t fDeviationCounter;
1576 uint16_t fDeviationMax;
1577
1578 vector<double> fDevBuffer;
1579 uint64_t fDevCount;
1580
1581 uint64_t fTrackingCounter;
1582
1583
1584 // --------------------- DIM Sending ------------------
1585
1586 bool CheckEventSize(size_t has, const char *name, size_t size)
1587 {
1588 if (has==size)
1589 return true;
1590
1591 ostringstream msg;
1592 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1593 T::Fatal(msg);
1594 return false;
1595 }
1596
1597 // --------------------- DIM Receiving ------------------
1598
1599 int HandleWeatherData(const EventImp &evt)
1600 {
1601 if (!CheckEventSize(evt.GetSize(), "HandleWeatherData", 7*4+2))
1602 {
1603 fWeather.time = Time(Time::none);
1604 return T::GetCurrentState();
1605 }
1606
1607 const float *ptr = evt.Ptr<float>(2);
1608
1609 fWeather.temp = ptr[0];
1610 fWeather.hum = ptr[2];
1611 fWeather.press = ptr[3];
1612 fWeather.time = evt.GetTime();
1613
1614 return T::GetCurrentState();
1615 }
1616
1617 int HandleTPoint(const EventImp &evt)
1618 {
1619 // Skip disconnect events
1620 if (evt.GetSize()==0)
1621 return T::GetCurrentState();
1622
1623 // skip invalid events
1624 if (!CheckEventSize(evt.GetSize(), "HandleTPoint", 11*8))
1625 return T::GetCurrentState();
1626
1627 // skip event which are older than one minute
1628 if (Time().UnixTime()-evt.GetTime().UnixTime()>60)
1629 return T::GetCurrentState();
1630
1631 // Original code in slaTps2c:
1632 //
1633 // From the tangent plane coordinates of a star of known RA,Dec,
1634 // determine the RA,Dec of the tangent point.
1635
1636 const double *ptr = evt.Ptr<double>();
1637
1638 // Tangent plane rectangular coordinates
1639 const double dx = ptr[0] * M_PI/648000; // [arcsec -> rad]
1640 const double dy = ptr[1] * M_PI/648000; // [arcsec -> rad]
1641
1642 const PointingData data = fPointingModel.CalcPointingPos(fPointingSetup, evt.GetTime().Mjd(), fWeather, fWeatherTimeout, true);
1643
1644 const double x2 = dx*dx;
1645 const double y2 = 1 + dy*dy;
1646
1647 const double sd = cos(data.sky.zd);//sin(M_PI/2-sky.zd);
1648 const double cd = sin(data.sky.zd);//cos(M_PI/2-sky.zd);
1649 const double sdf = sd*sqrt(x2+y2);
1650 const double r2 = cd*cd*y2 - sd*sd*x2;
1651
1652 // Case of no solution ("at the pole") or
1653 // two solutions ("over the pole solution")
1654 if (r2<0 || fabs(sdf)>=1)
1655 {
1656 T::Warn("Could not determine pointing direction from TPoint.");
1657 return T::GetCurrentState();
1658 }
1659
1660 const double r = sqrt(r2);
1661 const double s = sdf - dy * r;
1662 const double c = sdf * dy + r;
1663 const double phi = atan2(dx, r);
1664
1665 // Spherical coordinates of tangent point
1666 const double az = fmod(data.sky.az-phi + 2*M_PI, 2*M_PI);
1667 const double zd = M_PI/2 - atan2(s, c);
1668
1669 const Encoder dev = fDrive.GetSePos()*360 - data.mount;
1670
1671 // --- Output TPoint ---
1672
1673 const string fname = "tpoints-"+to_string(evt.GetTime().NightAsInt())+".txt";
1674 //time.GetAsStr("/%Y/%m/%d");
1675
1676 const bool exist = boost::filesystem::exists(fname);
1677
1678 ofstream fout(fname, ios::app);
1679 if (!exist)
1680 {
1681 fout << "FACT Model TPOINT data file" << endl;
1682 fout << ": ALTAZ" << endl;
1683 fout << "49 48 0 ";
1684 fout << evt.GetTime() << endl;
1685 }
1686 fout << setprecision(7);
1687 fout << fmod(az*180/M_PI+360, 360) << " ";
1688 fout << 90-zd*180/M_PI << " ";
1689 fout << fmod(data.mount.az+360, 360) << " ";
1690 fout << 90-data.mount.zd << " ";
1691 fout << dev.az << " "; // delta az
1692 fout << -dev.zd << " "; // delta el
1693 fout << 90-data.sky.zd * 180/M_PI << " ";
1694 fout << data.sky.az * 180/M_PI << " ";
1695 fout << setprecision(10);
1696 fout << data.mjd << " ";
1697 fout << setprecision(7);
1698 fout << ptr[6] << " "; // center.mag
1699 fout << ptr[9] << " "; // star.mag
1700 fout << ptr[4] << " "; // center.x
1701 fout << ptr[5] << " "; // center.y
1702 fout << ptr[7] << " "; // star.x
1703 fout << ptr[8] << " "; // star.y
1704 fout << ptr[2] << " "; // num leds
1705 fout << ptr[3] << " "; // num rings
1706 fout << ptr[0] << " "; // dx (de-rotated)
1707 fout << ptr[1] << " "; // dy (de-rotated)
1708 fout << ptr[10] << " "; // rotation angle
1709 fout << fPointingSetup.source.mag << " ";
1710 fout << fPointingSetup.source.name;
1711 fout << endl;
1712
1713 DimTPoint dim;
1714 dim.fRa = data.pointing.ra * 12/M_PI;
1715 dim.fDec = data.pointing.dec * 180/M_PI;
1716 dim.fNominalZd = data.sky.zd * 180/M_PI;
1717 dim.fNominalAz = data.sky.az * 180/M_PI;
1718 dim.fPointingZd = zd * 180/M_PI;
1719 dim.fPointingAz = az * 180/M_PI;
1720 dim.fFeedbackZd = data.mount.zd;
1721 dim.fFeedbackAz = data.mount.az;
1722 dim.fNumLeds = uint16_t(ptr[2]);
1723 dim.fNumRings = uint16_t(ptr[3]);
1724 dim.fCenterX = ptr[4];
1725 dim.fCenterY = ptr[5];
1726 dim.fCenterMag = ptr[6];
1727 dim.fStarX = ptr[7];
1728 dim.fStarY = ptr[8];
1729 dim.fStarMag = ptr[9];
1730 dim.fRotation = ptr[10];
1731 dim.fDx = ptr[0];
1732 dim.fDy = ptr[1];
1733 dim.fRealMag = fPointingSetup.source.mag;
1734
1735 fDrive.UpdateTPoint(evt.GetTime(), dim, fPointingSetup.source.name);
1736
1737 ostringstream txt;
1738 txt << "TPoint recorded [" << zd*180/M_PI << "/" << az*180/M_PI << " | "
1739 << data.sky.zd*180/M_PI << "/" << data.sky.az*180/M_PI << " | "
1740 << data.mount.zd << "/" << data.mount.az << " | "
1741 << dx*180/M_PI << "/" << dy*180/M_PI << "]";
1742 T::Info(txt);
1743
1744 return T::GetCurrentState();
1745 }
1746
1747 // -------------------------- Helpers -----------------------------------
1748
1749 double GetDevAbs(double nomzd, double meszd, double devaz)
1750 {
1751 nomzd *= M_PI/180;
1752 meszd *= M_PI/180;
1753 devaz *= M_PI/180;
1754
1755 const double x = sin(meszd) * sin(nomzd) * cos(devaz);
1756 const double y = cos(meszd) * cos(nomzd);
1757
1758 return acos(x + y) * 180/M_PI;
1759 }
1760
1761 double ReadAngle(istream &in)
1762 {
1763 char sgn;
1764 uint16_t d, m;
1765 float s;
1766
1767 in >> sgn >> d >> m >> s;
1768
1769 const double ret = ((60.0 * (60.0 * (double)d + (double)m) + s))/3600.;
1770 return sgn=='-' ? -ret : ret;
1771 }
1772
1773 bool CheckRange(ZdAz pos)
1774 {
1775 if (pos.zd<fPointingMin.zd)
1776 {
1777 T::Error("Zenith distance "+to_string(pos.zd)+" below limit "+to_string(fPointingMin.zd));
1778 return false;
1779 }
1780
1781 if (pos.zd>fPointingMax.zd)
1782 {
1783 T::Error("Zenith distance "+to_string(pos.zd)+" exceeds limit "+to_string(fPointingMax.zd));
1784 return false;
1785 }
1786
1787 if (pos.az<fPointingMin.az)
1788 {
1789 T::Error("Azimuth angle "+to_string(pos.az)+" below limit "+to_string(fPointingMin.az));
1790 return false;
1791 }
1792
1793 if (pos.az>fPointingMax.az)
1794 {
1795 T::Error("Azimuth angle "+to_string(pos.az)+" exceeds limit "+to_string(fPointingMax.az));
1796 return false;
1797 }
1798
1799 return true;
1800 }
1801
1802 PointingData CalcPointingPos(double mjd)
1803 {
1804 return fPointingModel.CalcPointingPos(fPointingSetup, mjd, fWeather, fWeatherTimeout);
1805 }
1806
1807 // ----------------------------- SDO Commands ------------------------------
1808
1809 int RequestSdo(const EventImp &evt)
1810 {
1811 // FIXME: STop telescope
1812 if (!CheckEventSize(evt.GetSize(), "RequestSdo", 6))
1813 return T::kSM_FatalError;
1814
1815 const uint16_t node = evt.Get<uint16_t>();
1816 const uint16_t index = evt.Get<uint16_t>(2);
1817 const uint16_t subidx = evt.Get<uint16_t>(4);
1818
1819 if (node!=1 && node !=3)
1820 {
1821 T::Error("Node id must be 1 (az) or 3 (zd).");
1822 return T::GetCurrentState();
1823 }
1824
1825 if (subidx>0xff)
1826 {
1827 T::Error("Subindex must not be larger than 255.");
1828 return T::GetCurrentState();
1829 }
1830
1831 fDrive.RequestSdo(node, index, subidx);
1832
1833 return T::GetCurrentState();
1834 }
1835
1836 int SendSdo(const EventImp &evt)
1837 {
1838 if (!CheckEventSize(evt.GetSize(), "SendSdo", 6+8))
1839 return T::kSM_FatalError;
1840
1841 const uint16_t node = evt.Get<uint16_t>();
1842 const uint16_t index = evt.Get<uint16_t>(2);
1843 const uint16_t subidx = evt.Get<uint16_t>(4);
1844 const uint64_t value = evt.Get<uint64_t>(6);
1845
1846 if (node!=1 && node!=3)
1847 {
1848 T::Error("Node id must be 1 (az) or 3 (zd).");
1849 return T::GetCurrentState();
1850 }
1851
1852 if (subidx>0xff)
1853 {
1854 T::Error("Subindex must not be larger than 255.");
1855 return T::GetCurrentState();
1856 }
1857
1858 fDrive.SendSdo(node, index, subidx, value);
1859
1860 return T::GetCurrentState();
1861 }
1862
1863 // --------------------- Moving and tracking ---------------------
1864
1865 uint16_t fStep;
1866 bool fIsTracking;
1867 Acceleration fAccPointing;
1868 Acceleration fAccTracking;
1869 Acceleration fAccMax;
1870 double fMaxPointingResidual;
1871 double fPointingVelocity;
1872
1873 int InitMovement(const ZdAz &sky, bool tracking=false, const string &name="")
1874 {
1875 fMovementTarget = fPointingModel.SkyToMount(sky);
1876
1877 // Check whether bending is valid!
1878 if (!CheckRange(sky*(180/M_PI)))
1879 return StopMovement();
1880
1881 fStep = 0;
1882 fIsTracking = tracking;
1883
1884 fDrive.SetRpmMode(false); // *NEW* (Stop a previous tracking to avoid the pointing command to be ignored)
1885 fDrive.SetAcceleration(fAccPointing);
1886
1887 if (!tracking)
1888 fDrive.UpdateSource(Time(), name, false);
1889 else
1890 {
1891 const array<double, 5> dim =
1892 {{
1893 fPointingSetup.source.ra,
1894 fPointingSetup.source.dec,
1895 fPointingSetup.wobble_offset * 180/M_PI,
1896 fPointingSetup.wobble_angle * 180/M_PI,
1897 fPointingSetup.orbit_period * 24*60
1898 }};
1899 fDrive.UpdateSource(fPointingSetup.start, dim, fPointingSetup.source.name);
1900 }
1901
1902 return State::kMoving;
1903 }
1904
1905 int MoveTo(const EventImp &evt)
1906 {
1907 if (!CheckEventSize(evt.GetSize(), "MoveTo", 16))
1908 return T::kSM_FatalError;
1909
1910 const double *dat = evt.Ptr<double>();
1911
1912 ostringstream out;
1913 out << "Pointing telescope to Zd=" << dat[0] << "deg Az=" << dat[1] << "deg";
1914 T::Message(out);
1915
1916 return InitMovement(ZdAz(dat[0]*M_PI/180, dat[1]*M_PI/180));
1917 }
1918
1919 int InitTracking()
1920 {
1921 fPointingSetup.start = Time().Mjd();
1922
1923 const PointingData data = CalcPointingPos(fPointingSetup.start);
1924
1925 ostringstream out;
1926 out << "Tracking position now at Zd=" << data.sky.zd*180/M_PI << "deg Az=" << data.sky.az*180/M_PI << "deg";
1927 T::Info(out);
1928
1929 return InitMovement(data.sky, true);
1930 }
1931
1932 int StartTracking(const Source &src, double offset, double angle, double period=0)
1933 {
1934 ostringstream out;
1935 out << "Tracking Ra=" << src.ra << "h Dec=" << src.dec << "deg";
1936 if (!src.name.empty())
1937 out << " [" << src.name << "]";
1938 T::Info(out);
1939
1940 fPointingSetup.planet = kENone;
1941 fPointingSetup.source = src;
1942 fPointingSetup.orbit_period = period / 1440; // [min->day]
1943 fPointingSetup.wobble_angle = angle * M_PI/180; // [deg->rad]
1944 fPointingSetup.wobble_offset = offset * M_PI/180; // [deg->rad]
1945
1946 return InitTracking();
1947 }
1948
1949 int TrackCelest(const Planets_t &p)
1950 {
1951 switch (p)
1952 {
1953 case kEMoon: fPointingSetup.source.name = "Moon"; break;
1954 case kEVenus: fPointingSetup.source.name = "Venus"; break;
1955 case kEMars: fPointingSetup.source.name = "Mars"; break;
1956 case kEJupiter: fPointingSetup.source.name = "Jupiter"; break;
1957 case kESaturn: fPointingSetup.source.name = "Saturn"; break;
1958 default:
1959 T::Error("TrackCelest - Celestial object "+to_string(p)+" not yet supported.");
1960 return T::GetCurrentState();
1961 }
1962
1963 fPointingSetup.planet = p;
1964 fPointingSetup.wobble_offset = 0;
1965
1966 fDrive.UpdateSource(Time(), fPointingSetup.source.name, true);
1967
1968 return InitTracking();
1969 }
1970
1971 int Park()
1972 {
1973 ostringstream out;
1974 out << "Parking telescope at Zd=" << fParkingPos.zd << "deg Az=" << fParkingPos.az << "deg";
1975 T::Message(out);
1976
1977 const int rc = InitMovement(ZdAz(fParkingPos.zd*M_PI/180, fParkingPos.az*M_PI/180), false, "Park");
1978 return rc==State::kMoving ? State::kParking : rc;
1979 }
1980
1981 int Wobble(const EventImp &evt)
1982 {
1983 if (!CheckEventSize(evt.GetSize(), "Wobble", 32))
1984 return T::kSM_FatalError;
1985
1986 const double *dat = evt.Ptr<double>();
1987
1988 Source src;
1989 src.ra = dat[0];
1990 src.dec = dat[1];
1991 return StartTracking(src, dat[2], dat[3]);
1992 }
1993
1994 int Orbit(const EventImp &evt)
1995 {
1996 if (!CheckEventSize(evt.GetSize(), "Orbit", 40))
1997 return T::kSM_FatalError;
1998
1999 const double *dat = evt.Ptr<double>();
2000
2001 Source src;
2002 src.ra = dat[0];
2003 src.dec = dat[1];
2004 return StartTracking(src, dat[2], dat[3], dat[4]);
2005 }
2006
2007 const sources::const_iterator GetSourceFromDB(const char *ptr, const char *last)
2008 {
2009 if (find(ptr, last, '\0')==last)
2010 {
2011 T::Fatal("TrackWobble - The name transmitted by dim is not null-terminated.");
2012 throw uint32_t(T::kSM_FatalError);
2013 }
2014
2015 const string name(ptr);
2016
2017 const sources::const_iterator it = fSources.find(name);
2018 if (it==fSources.end())
2019 {
2020 T::Error("Source '"+name+"' not found in list.");
2021 throw uint32_t(T::GetCurrentState());
2022 }
2023
2024 return it;
2025 }
2026
2027 int TrackWobble(const EventImp &evt)
2028 {
2029 if (evt.GetSize()<2)
2030 {
2031 ostringstream msg;
2032 msg << "TrackWobble - Received event has " << evt.GetSize() << " bytes, but expected at least 3.";
2033 T::Fatal(msg);
2034 return T::kSM_FatalError;
2035 }
2036
2037 if (evt.GetSize()==2)
2038 {
2039 ostringstream msg;
2040 msg << "TrackWobble - Source name missing.";
2041 T::Error(msg);
2042 return T::GetCurrentState();
2043 }
2044
2045 const uint16_t wobble = evt.GetUShort();
2046 if (wobble!=1 && wobble!=2)
2047 {
2048 ostringstream msg;
2049 msg << "TrackWobble - Wobble id " << wobble << " undefined, only 1 and 2 allowed.";
2050 T::Error(msg);
2051 return T::GetCurrentState();
2052 }
2053
2054 const char *ptr = evt.Ptr<char>(2);
2055 const char *last = ptr+evt.GetSize()-2;
2056
2057 try
2058 {
2059 const sources::const_iterator it = GetSourceFromDB(ptr, last);
2060
2061 const Source &src = it->second;
2062 return StartTracking(src, src.offset, src.angles[wobble-1]);
2063 }
2064 catch (const uint32_t &e)
2065 {
2066 return e;
2067 }
2068 }
2069
2070 int StartTrackWobble(const char *ptr, size_t size, const double &offset=0, const double &angle=0, double time=0)
2071 {
2072 const char *last = ptr+size;
2073
2074 try
2075 {
2076 const sources::const_iterator it = GetSourceFromDB(ptr, last);
2077
2078 const Source &src = it->second;
2079 return StartTracking(src, offset<0?0.6/*src.offset*/:offset, angle, time);
2080 }
2081 catch (const uint32_t &e)
2082 {
2083 return e;
2084 }
2085 }
2086
2087 int Track(const EventImp &evt)
2088 {
2089 if (!CheckEventSize(evt.GetSize(), "Track", 16))
2090 return T::kSM_FatalError;
2091
2092 Source src;
2093
2094 src.name = "";
2095 src.ra = evt.Get<double>(0);
2096 src.dec = evt.Get<double>(8);
2097
2098 return StartTracking(src, 0, 0);
2099 }
2100
2101 int TrackSource(const EventImp &evt)
2102 {
2103 if (evt.GetSize()<16)
2104 {
2105 ostringstream msg;
2106 msg << "TrackOn - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2107 T::Fatal(msg);
2108 return T::kSM_FatalError;
2109 }
2110
2111 if (evt.GetSize()==16)
2112 {
2113 ostringstream msg;
2114 msg << "TrackOn - Source name missing.";
2115 T::Error(msg);
2116 return T::GetCurrentState();
2117 }
2118
2119 const double offset = evt.Get<double>(0);
2120 const double angle = evt.Get<double>(8);
2121
2122 return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, offset, angle);
2123 }
2124
2125 int TrackOn(const EventImp &evt)
2126 {
2127 if (evt.GetSize()==0)
2128 {
2129 ostringstream msg;
2130 msg << "TrackOn - Source name missing.";
2131 T::Error(msg);
2132 return T::GetCurrentState();
2133 }
2134
2135 return StartTrackWobble(evt.Ptr<char>(), evt.GetSize());
2136 }
2137
2138 int TrackOrbit(const EventImp &evt)
2139 {
2140 if (evt.GetSize()<16)
2141 {
2142 ostringstream msg;
2143 msg << "TrackOrbit - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2144 T::Fatal(msg);
2145 return T::kSM_FatalError;
2146 }
2147 if (evt.GetSize()==16)
2148 {
2149 ostringstream msg;
2150 msg << "TrackOrbit - Source name missing.";
2151 T::Error(msg);
2152 return T::GetCurrentState();
2153 }
2154
2155 const double angle = evt.Get<double>(0);
2156 const double time = evt.Get<double>(8);
2157
2158 return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, -1, angle, time);
2159 }
2160
2161 int StopMovement()
2162 {
2163 fDrive.SetAcceleration(fAccMax);
2164 fDrive.SetRpmMode(false);
2165
2166 fTrackingLoop.cancel();
2167
2168 fDrive.UpdateSource(Time(), "", false);
2169
2170 return State::kStopping;
2171 }
2172
2173 int ResetError()
2174 {
2175 const int rc = CheckState();
2176 return rc>0 ? rc : State::kInitialized;
2177 }
2178
2179 // --------------------- Others ---------------------
2180
2181 int TPoint()
2182 {
2183 T::Info("TPoint initiated.");
2184 Dim::SendCommandNB("TPOINT/EXECUTE");
2185 return T::GetCurrentState();
2186 }
2187
2188 int Screenshot(const EventImp &evt)
2189 {
2190 if (evt.GetSize()<2)
2191 {
2192 ostringstream msg;
2193 msg << "Screenshot - Received event has " << evt.GetSize() << " bytes, but expected at least 2.";
2194 T::Fatal(msg);
2195 return T::kSM_FatalError;
2196 }
2197
2198 if (evt.GetSize()==2)
2199 {
2200 ostringstream msg;
2201 msg << "Screenshot - Filename missing.";
2202 T::Error(msg);
2203 return T::GetCurrentState();
2204 }
2205
2206 T::Info("Screenshot initiated.");
2207 Dim::SendCommandNB("TPOINT/SCREENSHOT", evt.GetData(), evt.GetSize());
2208 return T::GetCurrentState();
2209 }
2210
2211 int SetLedBrightness(const EventImp &evt)
2212 {
2213 if (!CheckEventSize(evt.GetSize(), "SetLedBrightness", 8))
2214 return T::kSM_FatalError;
2215
2216 const uint32_t *led = evt.Ptr<uint32_t>();
2217
2218 fDrive.SetLedVoltage(led[0], led[1]);
2219
2220 return T::GetCurrentState();
2221 }
2222
2223 int SetLedsOff()
2224 {
2225 fDrive.SetLedVoltage(0, 0);
2226 return T::GetCurrentState();
2227 }
2228
2229 // --------------------- Internal ---------------------
2230
2231 int SetVerbosity(const EventImp &evt)
2232 {
2233 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 2))
2234 return T::kSM_FatalError;
2235
2236 fDrive.SetVerbosity(evt.GetUShort());
2237
2238 return T::GetCurrentState();
2239 }
2240
2241 int Print()
2242 {
2243 for (auto it=fSources.begin(); it!=fSources.end(); it++)
2244 {
2245 const string &name = it->first;
2246 const Source &src = it->second;
2247
2248 T::Out() << name << ",";
2249 T::Out() << src.ra << "," << src.dec << "," << src.offset << ",";
2250 T::Out() << src.angles[0] << "," << src.angles[1] << endl;
2251 }
2252 return T::GetCurrentState();
2253 }
2254
2255 int Unlock()
2256 {
2257 const int rc = CheckState();
2258 return rc<0 ? State::kInitialized : rc;
2259 }
2260
2261 int ReloadSources()
2262 {
2263 try
2264 {
2265 ReadDatabase();
2266 }
2267 catch (const exception &e)
2268 {
2269 T::Error("Reading sources from databse failed: "+string(e.what()));
2270 }
2271 return T::GetCurrentState();
2272 }
2273
2274 int Disconnect()
2275 {
2276 // Close all connections
2277 fDrive.PostClose(false);
2278
2279 /*
2280 // Now wait until all connection have been closed and
2281 // all pending handlers have been processed
2282 poll();
2283 */
2284
2285 return T::GetCurrentState();
2286 }
2287
2288 int Reconnect(const EventImp &evt)
2289 {
2290 // Close all connections to supress the warning in SetEndpoint
2291 fDrive.PostClose(false);
2292
2293 // Now wait until all connection have been closed and
2294 // all pending handlers have been processed
2295 ba::io_service::poll();
2296
2297 if (evt.GetBool())
2298 fDrive.SetEndpoint(evt.GetString());
2299
2300 // Now we can reopen the connection
2301 fDrive.PostClose(true);
2302
2303 return T::GetCurrentState();
2304 }
2305
2306 // ========================= Tracking code =============================
2307
2308 int UpdateTrackingPosition()
2309 {
2310 // First calculate deviation between
2311 // command position and nominal position
2312 //fPointing.mount = sepos; // [deg] ref pos for alignment
2313 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2314
2315 // Get current position and calculate deviation
2316 const Encoder sepos = fDrive.GetSePos()*360; // [deg]
2317 const Encoder dev = sepos - data.mount;
2318
2319 // Calculate absolut deviation on the sky
2320 const double absdev = GetDevAbs(data.mount.zd, sepos.zd, dev.az)*3600;
2321
2322 // Smoothing
2323 fDevBuffer[fDevCount++%5] = absdev;
2324
2325 // Calculate average
2326 const uint8_t cnt = fDevCount<5 ? fDevCount : 5;
2327 const double avgdev = accumulate(fDevBuffer.begin(), fDevBuffer.begin()+cnt, 0.)/cnt;
2328
2329 // Count the consecutive number of avgdev below fDeviationLimit
2330 if (avgdev<fDeviationLimit)
2331 fTrackingCounter++;
2332 else
2333 fTrackingCounter = 0;
2334
2335 const double ha = fmod(fDrive.GetSeTime(),1)*24 - Nova::ORM().lng/15;
2336
2337 array<double, 12> dim;
2338 dim[0] = data.pointing.ra * 12/M_PI; // Ra [h] optical axis
2339 dim[1] = data.pointing.dec * 180/M_PI; // Dec [deg] optical axis
2340 dim[2] = ha - data.pointing.ra; // Ha [h] optical axis
2341 dim[3] = data.source.ra * 12/M_PI; // SrcRa [h] source position
2342 dim[4] = data.source.dec * 180/M_PI; // SrcDec [deg] source position
2343 dim[5] = ha - data.source.ra; // SrcHa [h] source position
2344 dim[6] = data.sky.zd * 180/M_PI; // Zd [deg] optical axis
2345 dim[7] = data.sky.az * 180/M_PI; // Az [deg] optical axis
2346 dim[8] = dev.zd; // dZd [deg] control deviation
2347 dim[9] = dev.az; // dAz [deg] control deviation
2348 dim[10] = absdev; // dev [arcsec] absolute control deviation
2349 dim[11] = avgdev; // dev [arcsec] average control deviation
2350
2351 fDrive.UpdateTracking(fDrive.GetSeTime(), dim);
2352
2353 if (fDrive.GetVerbosity())
2354 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Deviation [deg] " << absdev << "\"|" << avgdev << "\"|" << fDevCount<< " dZd=" << dev.zd*3600 << "\" dAz=" << dev.az*3600 << "\"" << endl;
2355
2356 // Maximum deviation execeeded -> fall back to Tracking state
2357 if (T::GetCurrentState()==State::kOnTrack && avgdev>fDeviationMax)
2358 return State::kTracking;
2359
2360 // Condition for OnTrack state achieved -> enhance to OnTrack state
2361 if (T::GetCurrentState()==State::kTracking && fTrackingCounter>=fDeviationCounter)
2362 return State::kOnTrack;
2363
2364 // No state change
2365 return T::GetCurrentState();
2366 }
2367
2368 void UpdatePointingPosition()
2369 {
2370 const Encoder sepos = fDrive.GetSePos()*360; // [deg] ref pos for alignment
2371
2372 const ZdAz pos = fPointingModel.MountToSky(sepos);
2373
2374 array<double, 2> data;
2375 data[0] = pos.zd*180/M_PI; // Zd [deg]
2376 data[1] = pos.az*180/M_PI; // Az [deg]
2377 fDrive.UpdatePointing(fDrive.GetSeTime(), data);
2378
2379 if (fDrive.GetVerbosity())
2380 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Position [deg] " << pos.zd*180/M_PI << " " << pos.az*180/M_PI << endl;
2381 }
2382
2383 void TrackingLoop(const boost::system::error_code &error=boost::system::error_code())
2384 {
2385 if (error==ba::error::basic_errors::operation_aborted)
2386 return;
2387
2388 if (error)
2389 {
2390 ostringstream str;
2391 str << "TrackingLoop: " << error.message() << " (" << error << ")";// << endl;
2392 T::Error(str);
2393 return;
2394 }
2395
2396 if (T::GetCurrentState()!=State::kTracking &&
2397 T::GetCurrentState()!=State::kOnTrack)
2398 return;
2399
2400 //
2401 // Update speed as often as possible.
2402 // make sure, that dt is around 10 times larger than the
2403 // update time
2404 //
2405 // The loop should not be executed faster than the ramp of
2406 // a change in the velocity can be followed.
2407 //
2408 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(250));
2409
2410 const double mjd = Time().Mjd();
2411
2412 // I assume that it takes about 50ms for the value to be
2413 // transmitted and the drive needs time to follow as well (maybe
2414 // more than 50ms), therefore the calculated speec is calculated
2415 // for a moment 50ms in the future
2416 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2417 const PointingData data0 = CalcPointingPos(mjd-0.45/24/3600);
2418 const PointingData data1 = CalcPointingPos(mjd+0.55/24/3600);
2419
2420 const Encoder dest = data.mount *(1./360); // [rev]
2421 const Encoder dest0 = data0.mount*(1./360); // [rev]
2422 const Encoder dest1 = data1.mount*(1./360); // [rev]
2423
2424 if (!CheckRange(data1.sky))
2425 {
2426 StopMovement();
2427 T::HandleNewState(State::kAllowedRangeExceeded, 0, "by TrackingLoop");
2428 return;
2429 }
2430
2431 // Current position
2432 const Encoder sepos = fDrive.GetSePos(); // [rev]
2433
2434 // Now calculate the current velocity
2435 const Encoder dist = dest1 - dest0; // [rev] Distance between t-1s and t+1s
2436 const Velocity vel = dist/(1./60); // [rev/min] Actual velocity of the pointing position
2437
2438 const Encoder dev = sepos - dest; // [rev] Current control deviation
2439 const Velocity vt = vel - dev/(1./60); // [rev/min] Correct velocity by recent control deviation
2440 // correct control deviation with 5s
2441 if (fDrive.GetVerbosity()>1)
2442 {
2443 T::Out() << "Ideal position [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2444 T::Out() << "Encoder pos. [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2445 T::Out() << "Deviation [arcmin] " << dev.zd *360*60 << " " << dev.az *360*60 << endl;
2446 T::Out() << "Distance 1s [arcmin] " << dist.zd *360*60 << " " << dist.az *360*60 << endl;
2447 T::Out() << "Velocity 1s [rpm] " << vt.zd << " " << vt.az << endl;
2448 T::Out() << "Delta T (enc) [ms] " << fabs(mjd-fDrive.fPdoTime2[0].Mjd())*24*3600*1000 << endl;
2449 T::Out() << "Delta T (now) [ms] " << (Time().Mjd()-mjd)*24*3600*1000 << endl;
2450 }
2451
2452 // Tracking loop every 250ms
2453 // Vorsteuerung 2s
2454 // Delta T (enc) 5ms, every 5th, 25ms
2455 // Delta T (now) equal dist 5ms-35 plus equal dist 25-55 (0.2%-2% of 2s)
2456
2457 //
2458 // FIXME: check if the drive is fast enough to follow the star
2459 //
2460 // Velocity units (would be 100 for %)
2461
2462 fDrive.SetTrackingVelocity(vt);
2463
2464 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2465 this, ba::placeholders::error));
2466 }
2467
2468 // =====================================================================
2469
2470 int CheckState()
2471 {
2472 if (!fDrive.IsConnected())
2473 return State::kDisconnected;
2474
2475 if (!fDrive.IsOnline())
2476 return State::kUnavailable;
2477
2478 // FIXME: This can prevent parking in case e.g.
2479 // of e8029 Position limit exceeded
2480 if (fDrive.HasError())
2481 {
2482 if (T::GetCurrentState()==State::kOnTrack ||
2483 T::GetCurrentState()==State::kTracking ||
2484 T::GetCurrentState()==State::kMoving ||
2485 T::GetCurrentState()==State::kParking)
2486 return StopMovement();
2487
2488 if (T::GetCurrentState()==State::kStopping && fDrive.IsMoving())
2489 return State::kStopping;
2490
2491 return StateMachineImp::kSM_Error;
2492 }
2493
2494 // This can happen if one of the drives is not in RF.
2495 // Usually this only happens when the drive is not yet in RF
2496 // or an error was just cleared. Usually there is no way that
2497 // a drive goes below the RF state during operation without
2498 // a warning or error message.
2499 if (fDrive.IsOnline() && fDrive.IsBlocked())
2500 return State::kBlocked;
2501
2502 if (fDrive.IsOnline() && !fDrive.IsReady())
2503 return State::kAvailable;
2504
2505 // This is the case as soon as the init commands were send
2506 // after a connection to the SPS was established
2507 if (fDrive.IsOnline() && fDrive.IsReady() && !fDrive.IsInitialized())
2508 return State::kArmed;
2509
2510 return -1;
2511 }
2512
2513 int Execute()
2514 {
2515 const Time now;
2516 if (now>fSunRise && T::GetCurrentState()!=State::kParking)
2517 {
2518 fSunRise = now.GetNextSunRise();
2519
2520 ostringstream msg;
2521 msg << "Next sun-rise will be at " << fSunRise;
2522 T::Info(msg);
2523
2524 if (T::GetCurrentState()>State::kArmed && T::GetCurrentState()!=StateMachineImp::kError)
2525 return Park();
2526 }
2527
2528 if (T::GetCurrentState()==State::kLocked)
2529 return State::kLocked;
2530
2531 // FIXME: Send STOP if IsPositioning or RpmActive but no
2532 // Moving or Tracking state
2533
2534 const int rc = CheckState();
2535 if (rc>0)
2536 return rc;
2537
2538 // Once every second
2539 static time_t lastTime = 0;
2540 const time_t tm = time(NULL);
2541 if (lastTime!=tm && fDrive.IsInitialized())
2542 {
2543 lastTime=tm;
2544
2545 UpdatePointingPosition();
2546
2547 if (T::GetCurrentState()==State::kTracking || T::GetCurrentState()==State::kOnTrack)
2548 return UpdateTrackingPosition();
2549 }
2550
2551 if (T::GetCurrentState()==State::kStopping && !fDrive.IsMoving())
2552 return State::kArmed;
2553
2554 if ((T::GetCurrentState()==State::kMoving ||
2555 T::GetCurrentState()==State::kParking) && !fDrive.IsMoving())
2556 {
2557 if (fIsTracking && fStep==1)
2558 {
2559 // Init tracking
2560 fDrive.SetAcceleration(fAccTracking);
2561 fDrive.SetRpmMode(true);
2562
2563 fDevCount = 0;
2564 fTrackingCounter = 0;
2565
2566 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(1));
2567 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2568 this, ba::placeholders::error));
2569
2570 fPointingSetup.start = Time().Mjd();
2571
2572 const PointingData data = CalcPointingPos(fPointingSetup.start);
2573
2574 ostringstream out;
2575 out << "Start tracking at Ra=" << data.pointing.ra*12/M_PI << "h Dec=" << data.pointing.dec*180/M_PI << "deg";
2576 T::Info(out);
2577
2578 return State::kTracking;
2579 }
2580
2581 // Get feedback 2
2582 const Encoder dest = fMovementTarget*(1./360); // [rev]
2583 const Encoder sepos = fDrive.GetSePos(); // [rev]
2584
2585 // Calculate residual to move deviation
2586 const Encoder dist = dest - sepos; // [rev]
2587
2588 // Check which axis should still be moved
2589 Encoder cd = dist; // [rev]
2590 cd *= 1./fMaxPointingResidual; // Scale to units of the maximum residual
2591 cd = cd.Abs();
2592
2593 // Check if there is a control deviation on the axis
2594 const bool cdzd = cd.zd>1;
2595 const bool cdaz = cd.az>1;
2596
2597 if (!fIsTracking)
2598 {
2599 // check if we reached the correct position already
2600 if (!cdzd && !cdaz)
2601 {
2602 T::Info("Target position reached in "+to_string(fStep)+" steps.");
2603 return T::GetCurrentState()==State::kParking ? State::kLocked : State::kArmed;
2604 }
2605
2606 if (fStep==10)
2607 {
2608 T::Error("Target position not reached in "+to_string(fStep)+" steps.");
2609 return State::kPositioningFailed;
2610 }
2611 }
2612
2613 const Encoder t = dist.Abs()/fDrive.GetVelUnit();
2614
2615 const Velocity vel =
2616 t.zd > t.az ?
2617 Velocity(1, t.zd==0?0:t.az/t.zd) :
2618 Velocity(t.az==0?0:t.zd/t.az, 1);
2619
2620 if (fDrive.GetVerbosity())
2621 {
2622 T::Out() << "Moving step " << fStep << endl;
2623 T::Out() << "Encoder [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2624 T::Out() << "Destination [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2625 T::Out() << "Residual [deg] " << dist.zd *360 << " " << dist.az *360 << endl;
2626 T::Out() << "Residual/max [1] " << cd.zd << " " << cd.az << endl;
2627 T::Out() << "Rel. time [1] " << t.zd << " " << t.az << endl;
2628 T::Out() << "Rel. velocity [1] " << vel.zd << " " << vel.az << endl;
2629 }
2630
2631 fDrive.SetPointingVelocity(vel, fPointingVelocity);
2632 fDrive.StartAbsolutePositioning(dest, cdzd, cdaz);
2633
2634 ostringstream out;
2635 if (fStep==0)
2636 out << "Moving to encoder Zd=" << dest.zd*360 << "deg Az=" << dest.az*360 << "deg";
2637 else
2638 out << "Moving residual of dZd=" << dist.zd*360*60 << "' dAz=" << dist.az*360*60 << "'";
2639 T::Info(out);
2640
2641 fStep++;
2642 }
2643
2644 return T::GetCurrentState()>=State::kInitialized ?
2645 T::GetCurrentState() : State::kInitialized;
2646 }
2647
2648public:
2649 StateMachineDrive(ostream &out=cout) :
2650 StateMachineAsio<T>(out, "DRIVE_CONTROL"), fDrive(*this, *this),
2651 fTrackingLoop(*this), fSunRise(Time().GetNextSunRise()), fDevBuffer(5)
2652 {
2653
2654 T::Subscribe("MAGIC_WEATHER/DATA")
2655 (bind(&StateMachineDrive::HandleWeatherData, this, placeholders::_1));
2656
2657 T::Subscribe("TPOINT/DATA")
2658 (bind(&StateMachineDrive::HandleTPoint, this, placeholders::_1));
2659
2660 // State names
2661 T::AddStateName(State::kDisconnected, "Disconnected",
2662 "No connection to SPS");
2663 T::AddStateName(State::kConnected, "Connected",
2664 "Connection to SPS, no information received yet");
2665
2666 T::AddStateName(State::kLocked, "Locked",
2667 "Drive system is locked (will not accept commands)");
2668
2669 T::AddStateName(State::kUnavailable, "Unavailable",
2670 "Connected to SPS, no connection to at least one IndraDrives");
2671 T::AddStateName(State::kAvailable, "Available",
2672 "Connected to SPS and to IndraDrives, but at least one drive not in RF");
2673 T::AddStateName(State::kBlocked, "Blocked",
2674 "Drive system is blocked by manual operation or a pressed emergeny button");
2675 T::AddStateName(State::kArmed, "Armed",
2676 "Connected to SPS and IndraDrives in RF, but not yet initialized");
2677 T::AddStateName(State::kInitialized, "Initialized",
2678 "Connected to SPS and IndraDrives in RF and initialized");
2679
2680 T::AddStateName(State::kStopping, "Stopping",
2681 "Stop command sent, waiting for telescope to be still");
2682 T::AddStateName(State::kParking, "Parking",
2683 "Telescope in parking operation, waiting for telescope to be still");
2684 T::AddStateName(State::kMoving, "Moving",
2685 "Telescope moving");
2686 T::AddStateName(State::kTracking, "Tracking",
2687 "Telescope in tracking mode");
2688 T::AddStateName(State::kOnTrack, "OnTrack",
2689 "Telescope tracking stable");
2690
2691 T::AddStateName(State::kPositioningFailed, "PositioningFailed",
2692 "Target position was not reached within ten steps");
2693 T::AddStateName(State::kAllowedRangeExceeded, "OutOfRange",
2694 "Telecope went out of range during tracking");
2695
2696
2697 T::AddEvent("REQUEST_SDO", "S:3", State::kArmed)
2698 (bind(&StateMachineDrive::RequestSdo, this, placeholders::_1))
2699 ("Request an SDO from the drive"
2700 "|node[uint32]:Node identifier (1:az, 3:zd)"
2701 "|index[uint32]:SDO index"
2702 "|subindex[uint32]:SDO subindex");
2703
2704 T::AddEvent("SET_SDO", "S:3;X:1", State::kArmed)
2705 (bind(&StateMachineDrive::SendSdo, this, placeholders::_1))
2706 ("Request an SDO from the drive"
2707 "|node[uint32]:Node identifier (1:az, 3:zd)"
2708 "|index[uint32]:SDO index"
2709 "|subindex[uint32]:SDO subindex"
2710 "|value[uint64]:Value");
2711
2712 // Drive Commands
2713 T::AddEvent("MOVE_TO", "D:2", State::kInitialized) // ->ZDAZ
2714 (bind(&StateMachineDrive::MoveTo, this, placeholders::_1))
2715 ("Move the telescope to the given local sky coordinates"
2716 "|Zd[deg]:Zenith distance"
2717 "|Az[deg]:Azimuth");
2718
2719 T::AddEvent("TRACK", "D:2", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2720 (bind(&StateMachineDrive::Track, this, placeholders::_1))
2721 ("Move the telescope to the given sky coordinates and start tracking them"
2722 "|Ra[h]:Right ascension"
2723 "|Dec[deg]:Declination");
2724
2725 T::AddEvent("WOBBLE", "D:4", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2726 (bind(&StateMachineDrive::Wobble, this, placeholders::_1))
2727 ("Move the telescope to the given wobble position around the given sky coordinates and start tracking them"
2728 "|Ra[h]:Right ascension"
2729 "|Dec[deg]:Declination"
2730 "|Offset[deg]:Wobble offset"
2731 "|Angle[deg]:Wobble angle");
2732
2733 T::AddEvent("ORBIT", "D:5", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2734 (bind(&StateMachineDrive::Orbit, this, placeholders::_1))
2735 ("Move the telescope in a circle around the source"
2736 "|Ra[h]:Right ascension"
2737 "|Dec[deg]:Declination"
2738 "|Offset[deg]:Wobble offset"
2739 "|Angle[deg]:Starting angle"
2740 "|Period[min]:Time for one orbit");
2741
2742 T::AddEvent("TRACK_SOURCE", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2743 (bind(&StateMachineDrive::TrackSource, this, placeholders::_1))
2744 ("Move the telescope to the given wobble position around the given source and start tracking"
2745 "|Offset[deg]:Wobble offset"
2746 "|Angle[deg]:Wobble angle"
2747 "|Name[string]:Source name");
2748
2749 T::AddEvent("TRACK_WOBBLE", "S:1;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2750 (bind(&StateMachineDrive::TrackWobble, this, placeholders::_1))
2751 ("Move the telescope to the given wobble position around the given source and start tracking"
2752 "|Id:Wobble angle id (1 or 2)"
2753 "|Name[string]:Source name");
2754
2755 T::AddEvent("TRACK_ORBIT", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2756 (bind(&StateMachineDrive::TrackOrbit, this, placeholders::_1))
2757 ("Move the telescope in a circle around the source"
2758 "|Angle[deg]:Starting angle"
2759 "|Period[min]:Time for one orbit"
2760 "|Name[string]:Source name");
2761
2762 T::AddEvent("TRACK_ON", "C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2763 (bind(&StateMachineDrive::TrackOn, this, placeholders::_1))
2764 ("Move the telescope to the given position and start tracking"
2765 "|Name[string]:Source name");
2766
2767 T::AddEvent("MOON", State::kInitialized, State::kTracking, State::kOnTrack)
2768 (bind(&StateMachineDrive::TrackCelest, this, kEMoon))
2769 ("Start tracking the moon");
2770 T::AddEvent("VENUS", State::kInitialized, State::kTracking, State::kOnTrack)
2771 (bind(&StateMachineDrive::TrackCelest, this, kEVenus))
2772 ("Start tracking Venus");
2773 T::AddEvent("MARS", State::kInitialized, State::kTracking, State::kOnTrack)
2774 (bind(&StateMachineDrive::TrackCelest, this, kEMars))
2775 ("Start tracking Mars");
2776 T::AddEvent("JUPITER", State::kInitialized, State::kTracking, State::kOnTrack)
2777 (bind(&StateMachineDrive::TrackCelest, this, kEJupiter))
2778 ("Start tracking Jupiter");
2779 T::AddEvent("SATURN", State::kInitialized, State::kTracking, State::kOnTrack)
2780 (bind(&StateMachineDrive::TrackCelest, this, kESaturn))
2781 ("Start tracking Saturn");
2782
2783 // FIXME: What to do in error state?
2784 T::AddEvent("PARK", State::kInitialized, State::kMoving, State::kTracking, State::kOnTrack)
2785 (bind(&StateMachineDrive::Park, this))
2786 ("Park the telescope");
2787
2788 T::AddEvent("STOP")(State::kUnavailable)(State::kAvailable)(State::kArmed)(State::kInitialized)(State::kStopping)(State::kParking)(State::kMoving)(State::kTracking)(State::kOnTrack)
2789 (bind(&StateMachineDrive::StopMovement, this))
2790 ("Stop any kind of movement.");
2791
2792 T::AddEvent("RESET", State::kPositioningFailed, State::kAllowedRangeExceeded)
2793 (bind(&StateMachineDrive::ResetError, this))
2794 ("Acknoledge an internal error (PositioningFailed, AllowedRangeExceeded)");
2795
2796 T::AddEvent("TPOINT", State::kOnTrack)
2797 (bind(&StateMachineDrive::TPoint, this))
2798 ("Take a TPoint");
2799
2800 T::AddEvent("SCREENSHOT", "B:1;C")
2801 (bind(&StateMachineDrive::Screenshot, this, placeholders::_1))
2802 ("Take a screenshot"
2803 "|color[bool]:False if just the gray image should be saved."
2804 "|name[string]:Filename");
2805
2806 T::AddEvent("SET_LED_BRIGHTNESS", "I:2")
2807 (bind(&StateMachineDrive::SetLedBrightness, this, placeholders::_1))
2808 ("Set the LED brightness of the top and bottom leds"
2809 "|top[au]:Allowed range 0-32767 for top LEDs"
2810 "|bot[au]:Allowed range 0-32767 for bottom LEDs");
2811
2812 T::AddEvent("LEDS_OFF")
2813 (bind(&StateMachineDrive::SetLedsOff, this))
2814 ("Switch off TPoint LEDs");
2815
2816 T::AddEvent("UNLOCK", Drive::State::kLocked)
2817 (bind(&StateMachineDrive::Unlock, this))
2818 ("Unlock locked state.");
2819
2820 // Verbosity commands
2821 T::AddEvent("SET_VERBOSITY", "S:1")
2822 (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1))
2823 ("Set verbosity state"
2824 "|verbosity[uint16]:disable or enable verbosity for received data (yes/no), except dynamic data");
2825
2826 // Conenction commands
2827 T::AddEvent("DISCONNECT", State::kConnected)
2828 (bind(&StateMachineDrive::Disconnect, this))
2829 ("disconnect from ethernet");
2830
2831 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
2832 (bind(&StateMachineDrive::Reconnect, this, placeholders::_1))
2833 ("(Re)connect Ethernet connection to SPS, a new address can be given"
2834 "|[host][string]:new ethernet address in the form <host:port>");
2835
2836
2837 T::AddEvent("PRINT")
2838 (bind(&StateMachineDrive::Print, this))
2839 ("Print source list.");
2840
2841 T::AddEvent("RELOAD_SOURCES", State::kDisconnected, State::kConnected, State::kArmed, State::kInitialized, State::kLocked)
2842 (bind(&StateMachineDrive::ReloadSources, this))
2843 ("Reload sources from database after database has changed..");
2844
2845
2846 //fDrive.SetUpdateStatus(std::bind(&StateMachineDrive::UpdateStatus, this, placeholders::_1, placeholders::_2));
2847 fDrive.StartConnect();
2848 }
2849
2850 void SetEndpoint(const string &url)
2851 {
2852 fDrive.SetEndpoint(url);
2853 }
2854
2855 bool AddSource(const string &name, const Source &src)
2856 {
2857 const auto it = fSources.find(name);
2858 if (it!=fSources.end())
2859 T::Warn("Source '"+name+"' already in list... overwriting.");
2860
2861 fSources[name] = src;
2862 return it==fSources.end();
2863 }
2864
2865 void ReadDatabase(bool print=true)
2866 {
2867#ifdef HAVE_SQL
2868 Database db(fDatabase);
2869
2870 T::Message("Connected to '"+db.uri()+"'");
2871
2872 const mysqlpp::StoreQueryResult res =
2873 db.query("SELECT fSourceName, fRightAscension, fDeclination, fWobbleOffset, fWobbleAngle0, fWobbleAngle1, fMagnitude FROM Source").store();
2874
2875 fSources.clear();
2876 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2877 {
2878 const string name = (*v)[0].c_str();
2879
2880 Source src;
2881 src.name = name;
2882 src.ra = (*v)[1];
2883 src.dec = (*v)[2];
2884 src.offset = (*v)[3];
2885 src.angles[0] = (*v)[4];
2886 src.angles[1] = (*v)[5];
2887 src.mag = (*v)[6] ? double((*v)[6]) : 0;
2888 AddSource(name, src);
2889
2890 if (!print)
2891 continue;
2892
2893 ostringstream msg;
2894 msg << " " << name << setprecision(8) << ": Ra=" << src.ra << "h Dec=" << src.dec << "deg";
2895 msg << " Wobble=[" << src.offset << "," << src.angles[0] << "," << src.angles[1] << "] Mag=" << src.mag;
2896 T::Message(msg);
2897 }
2898#else
2899 T::Warn("MySQL support not compiled into the program.");
2900#endif
2901 }
2902
2903 int EvalOptions(Configuration &conf)
2904 {
2905 if (!fSunRise)
2906 return 1;
2907
2908 fDrive.SetVerbose(!conf.Get<bool>("quiet"));
2909
2910 fMaxPointingResidual = conf.Get<double>("pointing.max.residual");
2911 fPointingVelocity = conf.Get<double>("pointing.velocity");
2912
2913 fPointingMin = Encoder(conf.Get<double>("pointing.min.zd"),
2914 conf.Get<double>("pointing.min.az"));
2915 fPointingMax = Encoder(conf.Get<double>("pointing.max.zd"),
2916 conf.Get<double>("pointing.max.az"));
2917
2918 fParkingPos.zd = conf.Has("parking-pos.zd") ? conf.Get<double>("parking-pos.zd") : 90;
2919 fParkingPos.az = conf.Has("parking-pos.az") ? conf.Get<double>("parking-pos.az") : 0;
2920
2921 if (!CheckRange(fParkingPos))
2922 return 2;
2923
2924 fAccPointing = Acceleration(conf.Get<double>("pointing.acceleration.zd"),
2925 conf.Get<double>("pointing.acceleration.az"));
2926 fAccTracking = Acceleration(conf.Get<double>("tracking.acceleration.zd"),
2927 conf.Get<double>("tracking.acceleration.az"));
2928 fAccMax = Acceleration(conf.Get<double>("acceleration.max.zd"),
2929 conf.Get<double>("acceleration.max.az"));
2930
2931 fWeatherTimeout = conf.Get<uint16_t>("weather-timeout");
2932
2933 if (fAccPointing>fAccMax)
2934 {
2935 T::Error("Pointing acceleration exceeds maximum acceleration.");
2936 return 3;
2937 }
2938
2939 if (fAccTracking>fAccMax)
2940 {
2941 T::Error("Tracking acceleration exceeds maximum acceleration.");
2942 return 4;
2943 }
2944
2945 fDeviationLimit = conf.Get<uint16_t>("deviation-limit");
2946 fDeviationCounter = conf.Get<uint16_t>("deviation-count");
2947 fDeviationMax = conf.Get<uint16_t>("deviation-max");
2948
2949 const string fname = conf.Get<string>("pointing.model-file");
2950
2951 try
2952 {
2953 fPointingModel.Load(fname);
2954 }
2955 catch (const exception &e)
2956 {
2957 T::Error(e.what());
2958 return 5;
2959 }
2960
2961 const vector<string> &vec = conf.Vec<string>("source");
2962
2963 for (vector<string>::const_iterator it=vec.begin(); it!=vec.end(); it++)
2964 {
2965 istringstream stream(*it);
2966
2967 string name;
2968
2969 int i=0;
2970
2971 Source src;
2972
2973 string buffer;
2974 while (getline(stream, buffer, ','))
2975 {
2976 istringstream is(buffer);
2977
2978 switch (i++)
2979 {
2980 case 0: name = buffer; break;
2981 case 1: src.ra = ReadAngle(is); break;
2982 case 2: src.dec = ReadAngle(is); break;
2983 case 3: is >> src.offset; break;
2984 case 4: is >> src.angles[0]; break;
2985 case 5: is >> src.angles[1]; break;
2986 }
2987
2988 if (is.fail())
2989 break;
2990 }
2991
2992 if (i==3 || i==6)
2993 {
2994 AddSource(name, src);
2995 continue;
2996 }
2997
2998 T::Warn("Resource 'source' not correctly formatted: '"+*it+"'");
2999 }
3000
3001 //fAutoResume = conf.Get<bool>("auto-resume");
3002
3003 if (conf.Has("source-database"))
3004 {
3005 fDatabase = conf.Get<string>("source-database");
3006 ReadDatabase();
3007 }
3008
3009 if (fSunRise.IsValid())
3010 {
3011 ostringstream msg;
3012 msg << "Next sun-rise will be at " << fSunRise;
3013 T::Message(msg);
3014 }
3015
3016 // The possibility to connect should be last, so that
3017 // everything else is already initialized.
3018 SetEndpoint(conf.Get<string>("addr"));
3019
3020 return -1;
3021 }
3022};
3023
3024// ------------------------------------------------------------------------
3025
3026#include "Main.h"
3027
3028
3029template<class T, class S, class R>
3030int RunShell(Configuration &conf)
3031{
3032 return Main::execute<T, StateMachineDrive<S, R>>(conf);
3033}
3034
3035void SetupConfiguration(Configuration &conf)
3036{
3037 po::options_description control("Drive control options");
3038 control.add_options()
3039 ("quiet,q", po_bool(), "Disable debug messages")
3040 ("no-dim,d", po_switch(), "Disable dim services")
3041 ("addr,a", var<string>("sps:5357"), "Network address of cosy")
3042 ("verbosity,v", var<uint16_t>(0), "Vervosity level (0=off; 1=major updates; 2=most updates; 3=frequent updates)")
3043 ("pointing.model-file", var<string>()->required(), "Name of the file with the pointing model in use")
3044 ("pointing.max.zd", var<double>( 104.9), "Maximum allowed zenith angle in sky pointing coordinates [deg]")
3045 ("pointing.max.az", var<double>( 85.0), "Maximum allowed azimuth angle in sky pointing coordinates [deg]")
3046 ("pointing.min.zd", var<double>(-104.9), "Minimum allowed zenith angle in sky pointing coordinates [deg]")
3047 ("pointing.min.az", var<double>(-295.0), "Minimum allowed azimuth angle in sky pointing coordinates [deg]")
3048 ("pointing.max.residual", var<double>(1./32768), "Maximum residual for a pointing operation [revolutions]")
3049 ("pointing.velocity", var<double>(0.3), "Moving velocity when pointing [% max]")
3050 ("pointing.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis for pointing operations")
3051 ("pointing.acceleration.zd", var<double>(0.03), "Acceleration for zenith axis for pointing operations")
3052 ("tracking.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis during tracking operations")
3053 ("tracking.acceleration.zd", var<double>(0.01), "Acceleration for zenith axis during tracking operations")
3054 ("parking-pos.zd", var<double>(101), "Parking position zenith angle in sky pointing coordinates [deg]")
3055 ("parking-pos.az", var<double>(0), "Parking position azimuth angle in sky pointing coordinates [deg]")
3056 ("acceleration.max.az", var<double>(0.03), "Maximum allowed acceleration value for azimuth axis")
3057 ("acceleration.max.zd", var<double>(0.09), "Maximum allowed acceleration value for zenith axis")
3058 ("weather-timeout", var<uint16_t>(300), "Timeout [sec] for weather data (after timeout default values are used)")
3059 ("deviation-limit", var<uint16_t>(90), "Deviation limit in arcsec to get 'OnTrack'")
3060 ("deviation-count", var<uint16_t>(3), "Minimum number of reported deviation below deviation-limit to get 'OnTrack'")
3061 ("deviation-max", var<uint16_t>(180), "Maximum deviation in arcsec allowed to keep status 'OnTrack'")
3062 ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
3063 ("source", vars<string>(), "Additional source entry in the form \"name,hh:mm:ss,dd:mm:ss\"")
3064 ;
3065
3066 conf.AddOptions(control);
3067}
3068
3069/*
3070 Extract usage clause(s) [if any] for SYNOPSIS.
3071 Translators: "Usage" and "or" here are patterns (regular expressions) which
3072 are used to match the usage synopsis in program output. An example from cp
3073 (GNU coreutils) which contains both strings:
3074 Usage: cp [OPTION]... [-T] SOURCE DEST
3075 or: cp [OPTION]... SOURCE... DIRECTORY
3076 or: cp [OPTION]... -t DIRECTORY SOURCE...
3077 */
3078void PrintUsage()
3079{
3080 cout <<
3081 "The drivectrl is an interface to the drive PLC.\n"
3082 "\n"
3083 "The default is that the program is started without user intercation. "
3084 "All actions are supposed to arrive as DimCommands. Using the -c "
3085 "option, a local shell can be initialized. With h or help a short "
3086 "help message about the usuage can be brought to the screen.\n"
3087 "\n"
3088 "Usage: drivectrl [-c type] [OPTIONS]\n"
3089 " or: drivectrl [OPTIONS]\n";
3090 cout << endl;
3091}
3092
3093void PrintHelp()
3094{
3095 Main::PrintHelp<StateMachineDrive<StateMachine,ConnectionDrive>>();
3096
3097 /* Additional help text which is printed after the configuration
3098 options goes here */
3099
3100 /*
3101 cout << "bla bla bla" << endl << endl;
3102 cout << endl;
3103 cout << "Environment:" << endl;
3104 cout << "environment" << endl;
3105 cout << endl;
3106 cout << "Examples:" << endl;
3107 cout << "test exam" << endl;
3108 cout << endl;
3109 cout << "Files:" << endl;
3110 cout << "files" << endl;
3111 cout << endl;
3112 */
3113}
3114
3115int main(int argc, const char* argv[])
3116{
3117 Configuration conf(argv[0]);
3118 conf.SetPrintUsage(PrintUsage);
3119 Main::SetupConfiguration(conf);
3120 SetupConfiguration(conf);
3121
3122 if (!conf.DoParse(argc, argv, PrintHelp))
3123 return 127;
3124
3125 //try
3126 {
3127 // No console access at all
3128 if (!conf.Has("console"))
3129 {
3130 if (conf.Get<bool>("no-dim"))
3131 return RunShell<LocalStream, StateMachine, ConnectionDrive>(conf);
3132 else
3133 return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(conf);
3134 }
3135 // Cosole access w/ and w/o Dim
3136 if (conf.Get<bool>("no-dim"))
3137 {
3138 if (conf.Get<int>("console")==0)
3139 return RunShell<LocalShell, StateMachine, ConnectionDrive>(conf);
3140 else
3141 return RunShell<LocalConsole, StateMachine, ConnectionDrive>(conf);
3142 }
3143 else
3144 {
3145 if (conf.Get<int>("console")==0)
3146 return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
3147 else
3148 return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
3149 }
3150 }
3151 /*catch (std::exception& e)
3152 {
3153 cerr << "Exception: " << e.what() << endl;
3154 return -1;
3155 }*/
3156
3157 return 0;
3158}
Note: See TracBrowser for help on using the repository browser.