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

Last change on this file since 18380 was 18379, checked in by tbretz, 10 years ago
Exchanged order for checking for blocked and available.. otherwise blocked will always be shown as available, fixed a wrong state description, removed some states from allowed states for STOP. Esepcially in LOCKED it must not be allowed, because it would leave locked.
File size: 108.0 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 // --------------------- Others ---------------------
2174
2175 int TPoint()
2176 {
2177 T::Info("TPoint initiated.");
2178 Dim::SendCommandNB("TPOINT/EXECUTE");
2179 return T::GetCurrentState();
2180 }
2181
2182 int SetLedBrightness(const EventImp &evt)
2183 {
2184 if (!CheckEventSize(evt.GetSize(), "SetLedBrightness", 8))
2185 return T::kSM_FatalError;
2186
2187 const uint32_t *led = evt.Ptr<uint32_t>();
2188
2189 fDrive.SetLedVoltage(led[0], led[1]);
2190
2191 return T::GetCurrentState();
2192 }
2193
2194 int SetLedsOff()
2195 {
2196 fDrive.SetLedVoltage(0, 0);
2197 return T::GetCurrentState();
2198 }
2199
2200 // --------------------- Internal ---------------------
2201
2202 int SetVerbosity(const EventImp &evt)
2203 {
2204 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 2))
2205 return T::kSM_FatalError;
2206
2207 fDrive.SetVerbosity(evt.GetUShort());
2208
2209 return T::GetCurrentState();
2210 }
2211
2212 int Print()
2213 {
2214 for (auto it=fSources.begin(); it!=fSources.end(); it++)
2215 {
2216 const string &name = it->first;
2217 const Source &src = it->second;
2218
2219 T::Out() << name << ",";
2220 T::Out() << src.ra << "," << src.dec << "," << src.offset << ",";
2221 T::Out() << src.angles[0] << "," << src.angles[1] << endl;
2222 }
2223 return T::GetCurrentState();
2224 }
2225
2226 int Unlock()
2227 {
2228 const int rc = CheckState();
2229 return rc<0 ? State::kInitialized : rc;
2230 }
2231
2232 int ReloadSources()
2233 {
2234 try
2235 {
2236 ReadDatabase();
2237 }
2238 catch (const exception &e)
2239 {
2240 T::Error("Reading sources from databse failed: "+string(e.what()));
2241 }
2242 return T::GetCurrentState();
2243 }
2244
2245 int Disconnect()
2246 {
2247 // Close all connections
2248 fDrive.PostClose(false);
2249
2250 /*
2251 // Now wait until all connection have been closed and
2252 // all pending handlers have been processed
2253 poll();
2254 */
2255
2256 return T::GetCurrentState();
2257 }
2258
2259 int Reconnect(const EventImp &evt)
2260 {
2261 // Close all connections to supress the warning in SetEndpoint
2262 fDrive.PostClose(false);
2263
2264 // Now wait until all connection have been closed and
2265 // all pending handlers have been processed
2266 ba::io_service::poll();
2267
2268 if (evt.GetBool())
2269 fDrive.SetEndpoint(evt.GetString());
2270
2271 // Now we can reopen the connection
2272 fDrive.PostClose(true);
2273
2274 return T::GetCurrentState();
2275 }
2276
2277 // ========================= Tracking code =============================
2278
2279 int UpdateTrackingPosition()
2280 {
2281 // First calculate deviation between
2282 // command position and nominal position
2283 //fPointing.mount = sepos; // [deg] ref pos for alignment
2284 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2285
2286 // Get current position and calculate deviation
2287 const Encoder sepos = fDrive.GetSePos()*360; // [deg]
2288 const Encoder dev = sepos - data.mount;
2289
2290 // Calculate absolut deviation on the sky
2291 const double absdev = GetDevAbs(data.mount.zd, sepos.zd, dev.az)*3600;
2292
2293 // Smoothing
2294 fDevBuffer[fDevCount++%5] = absdev;
2295
2296 // Calculate average
2297 const uint8_t cnt = fDevCount<5 ? fDevCount : 5;
2298 const double avgdev = accumulate(fDevBuffer.begin(), fDevBuffer.begin()+cnt, 0.)/cnt;
2299
2300 // Count the consecutive number of avgdev below fDeviationLimit
2301 if (avgdev<fDeviationLimit)
2302 fTrackingCounter++;
2303 else
2304 fTrackingCounter = 0;
2305
2306 const double ha = fmod(fDrive.GetSeTime(),1)*24 - Nova::ORM().lng/15;
2307
2308 array<double, 12> dim;
2309 dim[0] = data.pointing.ra * 12/M_PI; // Ra [h] optical axis
2310 dim[1] = data.pointing.dec * 180/M_PI; // Dec [deg] optical axis
2311 dim[2] = ha - data.pointing.ra; // Ha [h] optical axis
2312 dim[3] = data.source.ra * 12/M_PI; // SrcRa [h] source position
2313 dim[4] = data.source.dec * 180/M_PI; // SrcDec [deg] source position
2314 dim[5] = ha - data.source.ra; // SrcHa [h] source position
2315 dim[6] = data.sky.zd * 180/M_PI; // Zd [deg] optical axis
2316 dim[7] = data.sky.az * 180/M_PI; // Az [deg] optical axis
2317 dim[8] = dev.zd; // dZd [deg] control deviation
2318 dim[9] = dev.az; // dAz [deg] control deviation
2319 dim[10] = absdev; // dev [arcsec] absolute control deviation
2320 dim[11] = avgdev; // dev [arcsec] average control deviation
2321
2322 fDrive.UpdateTracking(fDrive.GetSeTime(), dim);
2323
2324 if (fDrive.GetVerbosity())
2325 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Deviation [deg] " << absdev << "\"|" << avgdev << "\"|" << fDevCount<< " dZd=" << dev.zd*3600 << "\" dAz=" << dev.az*3600 << "\"" << endl;
2326
2327 // Maximum deviation execeeded -> fall back to Tracking state
2328 if (T::GetCurrentState()==State::kOnTrack && avgdev>fDeviationMax)
2329 return State::kTracking;
2330
2331 // Condition for OnTrack state achieved -> enhance to OnTrack state
2332 if (T::GetCurrentState()==State::kTracking && fTrackingCounter>=fDeviationCounter)
2333 return State::kOnTrack;
2334
2335 // No state change
2336 return T::GetCurrentState();
2337 }
2338
2339 void UpdatePointingPosition()
2340 {
2341 const Encoder sepos = fDrive.GetSePos()*360; // [deg] ref pos for alignment
2342
2343 const ZdAz pos = fPointingModel.MountToSky(sepos);
2344
2345 array<double, 2> data;
2346 data[0] = pos.zd*180/M_PI; // Zd [deg]
2347 data[1] = pos.az*180/M_PI; // Az [deg]
2348 fDrive.UpdatePointing(fDrive.GetSeTime(), data);
2349
2350 if (fDrive.GetVerbosity())
2351 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Position [deg] " << pos.zd*180/M_PI << " " << pos.az*180/M_PI << endl;
2352 }
2353
2354 void TrackingLoop(const boost::system::error_code &error=boost::system::error_code())
2355 {
2356 if (error==ba::error::basic_errors::operation_aborted)
2357 return;
2358
2359 if (error)
2360 {
2361 ostringstream str;
2362 str << "TrackingLoop: " << error.message() << " (" << error << ")";// << endl;
2363 T::Error(str);
2364 return;
2365 }
2366
2367 if (T::GetCurrentState()!=State::kTracking &&
2368 T::GetCurrentState()!=State::kOnTrack)
2369 return;
2370
2371 //
2372 // Update speed as often as possible.
2373 // make sure, that dt is around 10 times larger than the
2374 // update time
2375 //
2376 // The loop should not be executed faster than the ramp of
2377 // a change in the velocity can be followed.
2378 //
2379 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(250));
2380
2381 const double mjd = Time().Mjd();
2382
2383 // I assume that it takes about 50ms for the value to be
2384 // transmitted and the drive needs time to follow as well (maybe
2385 // more than 50ms), therefore the calculated speec is calculated
2386 // for a moment 50ms in the future
2387 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2388 const PointingData data0 = CalcPointingPos(mjd-0.45/24/3600);
2389 const PointingData data1 = CalcPointingPos(mjd+0.55/24/3600);
2390
2391 const Encoder dest = data.mount *(1./360); // [rev]
2392 const Encoder dest0 = data0.mount*(1./360); // [rev]
2393 const Encoder dest1 = data1.mount*(1./360); // [rev]
2394
2395 if (!CheckRange(data1.sky))
2396 {
2397 StopMovement();
2398 T::HandleNewState(State::kAllowedRangeExceeded, 0, "by TrackingLoop");
2399 return;
2400 }
2401
2402 // Current position
2403 const Encoder sepos = fDrive.GetSePos(); // [rev]
2404
2405 // Now calculate the current velocity
2406 const Encoder dist = dest1 - dest0; // [rev] Distance between t-1s and t+1s
2407 const Velocity vel = dist/(1./60); // [rev/min] Actual velocity of the pointing position
2408
2409 const Encoder dev = sepos - dest; // [rev] Current control deviation
2410 const Velocity vt = vel - dev/(1./60); // [rev/min] Correct velocity by recent control deviation
2411 // correct control deviation with 5s
2412 if (fDrive.GetVerbosity()>1)
2413 {
2414 T::Out() << "Ideal position [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2415 T::Out() << "Encoder pos. [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2416 T::Out() << "Deviation [arcmin] " << dev.zd *360*60 << " " << dev.az *360*60 << endl;
2417 T::Out() << "Distance 1s [arcmin] " << dist.zd *360*60 << " " << dist.az *360*60 << endl;
2418 T::Out() << "Velocity 1s [rpm] " << vt.zd << " " << vt.az << endl;
2419 T::Out() << "Delta T (enc) [ms] " << fabs(mjd-fDrive.fPdoTime2[0].Mjd())*24*3600*1000 << endl;
2420 T::Out() << "Delta T (now) [ms] " << (Time().Mjd()-mjd)*24*3600*1000 << endl;
2421 }
2422
2423 // Tracking loop every 250ms
2424 // Vorsteuerung 2s
2425 // Delta T (enc) 5ms, every 5th, 25ms
2426 // Delta T (now) equal dist 5ms-35 plus equal dist 25-55 (0.2%-2% of 2s)
2427
2428 //
2429 // FIXME: check if the drive is fast enough to follow the star
2430 //
2431 // Velocity units (would be 100 for %)
2432
2433 fDrive.SetTrackingVelocity(vt);
2434
2435 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2436 this, ba::placeholders::error));
2437 }
2438
2439 // =====================================================================
2440
2441 int CheckState()
2442 {
2443 if (!fDrive.IsConnected())
2444 return State::kDisconnected;
2445
2446 if (!fDrive.IsOnline())
2447 return State::kUnavailable;
2448
2449 // FIXME: This can prevent parking in case e.g.
2450 // of e8029 Position limit exceeded
2451 if (fDrive.HasError())
2452 {
2453 if (T::GetCurrentState()==State::kOnTrack ||
2454 T::GetCurrentState()==State::kTracking ||
2455 T::GetCurrentState()==State::kMoving ||
2456 T::GetCurrentState()==State::kParking)
2457 return StopMovement();
2458
2459 if (T::GetCurrentState()==State::kStopping && fDrive.IsMoving())
2460 return State::kStopping;
2461
2462 return StateMachineImp::kSM_Error;
2463 }
2464
2465 // This can happen if one of the drives is not in RF.
2466 // Usually this only happens when the drive is not yet in RF
2467 // or an error was just cleared. Usually there is no way that
2468 // a drive goes below the RF state during operation without
2469 // a warning or error message.
2470 if (fDrive.IsOnline() && fDrive.IsBlocked())
2471 return State::kBlocked;
2472
2473 if (fDrive.IsOnline() && !fDrive.IsReady())
2474 return State::kAvailable;
2475
2476 // This is the case as soon as the init commands were send
2477 // after a connection to the SPS was established
2478 if (fDrive.IsOnline() && fDrive.IsReady() && !fDrive.IsInitialized())
2479 return State::kArmed;
2480
2481 return -1;
2482 }
2483
2484 int Execute()
2485 {
2486 const Time now;
2487 if (now>fSunRise)
2488 {
2489 if (T::GetCurrentState()>State::kLocked)
2490 return Park();
2491
2492 if (T::GetCurrentState()==State::kLocked)
2493 {
2494 fSunRise = now.GetNextSunRise();
2495
2496 ostringstream msg;
2497 msg << "Next sun-rise will be at " << fSunRise;
2498 T::Info(msg);
2499
2500 return State::kLocked;
2501 }
2502 }
2503
2504 if (T::GetCurrentState()==State::kLocked)
2505 return State::kLocked;
2506
2507 // FIXME: Send STOP if IsPositioning or RpmActive but no
2508 // Moving or Tracking state
2509
2510 const int rc = CheckState();
2511 if (rc>0)
2512 return rc;
2513
2514 // Once every second
2515 static time_t lastTime = 0;
2516 const time_t tm = time(NULL);
2517 if (lastTime!=tm && fDrive.IsInitialized())
2518 {
2519 lastTime=tm;
2520
2521 UpdatePointingPosition();
2522
2523 if (T::GetCurrentState()==State::kTracking || T::GetCurrentState()==State::kOnTrack)
2524 return UpdateTrackingPosition();
2525 }
2526
2527 if (T::GetCurrentState()==State::kStopping && !fDrive.IsMoving())
2528 return State::kArmed;
2529
2530 if ((T::GetCurrentState()==State::kMoving ||
2531 T::GetCurrentState()==State::kParking) && !fDrive.IsMoving())
2532 {
2533 if (fIsTracking && fStep==1)
2534 {
2535 // Init tracking
2536 fDrive.SetAcceleration(fAccTracking);
2537 fDrive.SetRpmMode(true);
2538
2539 fDevCount = 0;
2540 fTrackingCounter = 0;
2541
2542 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(1));
2543 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2544 this, ba::placeholders::error));
2545
2546 fPointingSetup.start = Time().Mjd();
2547
2548 const PointingData data = CalcPointingPos(fPointingSetup.start);
2549
2550 ostringstream out;
2551 out << "Start tracking at Ra=" << data.pointing.ra*12/M_PI << "h Dec=" << data.pointing.dec*180/M_PI << "deg";
2552 T::Info(out);
2553
2554 return State::kTracking;
2555 }
2556
2557 // Get feedback 2
2558 const Encoder dest = fMovementTarget*(1./360); // [rev]
2559 const Encoder sepos = fDrive.GetSePos(); // [rev]
2560
2561 // Calculate residual to move deviation
2562 const Encoder dist = dest - sepos; // [rev]
2563
2564 // Check which axis should still be moved
2565 Encoder cd = dist; // [rev]
2566 cd *= 1./fMaxPointingResidual; // Scale to units of the maximum residual
2567 cd = cd.Abs();
2568
2569 // Check if there is a control deviation on the axis
2570 const bool cdzd = cd.zd>1;
2571 const bool cdaz = cd.az>1;
2572
2573 if (!fIsTracking)
2574 {
2575 // check if we reached the correct position already
2576 if (!cdzd && !cdaz)
2577 {
2578 T::Info("Target position reached in "+to_string(fStep)+" steps.");
2579 return T::GetCurrentState()==State::kParking ? State::kLocked : State::kArmed;
2580 }
2581
2582 if (fStep==10)
2583 {
2584 T::Error("Target position not reached in "+to_string(fStep)+" steps.");
2585 return State::kPositioningFailed;
2586 }
2587 }
2588
2589 const Encoder t = dist.Abs()/fDrive.GetVelUnit();
2590
2591 const Velocity vel =
2592 t.zd > t.az ?
2593 Velocity(1, t.zd==0?0:t.az/t.zd) :
2594 Velocity(t.az==0?0:t.zd/t.az, 1);
2595
2596 if (fDrive.GetVerbosity())
2597 {
2598 T::Out() << "Moving step " << fStep << endl;
2599 T::Out() << "Encoder [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2600 T::Out() << "Destination [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2601 T::Out() << "Residual [deg] " << dist.zd *360 << " " << dist.az *360 << endl;
2602 T::Out() << "Residual/max [1] " << cd.zd << " " << cd.az << endl;
2603 T::Out() << "Rel. time [1] " << t.zd << " " << t.az << endl;
2604 T::Out() << "Rel. velocity [1] " << vel.zd << " " << vel.az << endl;
2605 }
2606
2607 fDrive.SetPointingVelocity(vel, fPointingVelocity);
2608 fDrive.StartAbsolutePositioning(dest, cdzd, cdaz);
2609
2610 ostringstream out;
2611 if (fStep==0)
2612 out << "Moving to encoder Zd=" << dest.zd*360 << "deg Az=" << dest.az*360 << "deg";
2613 else
2614 out << "Moving residual of dZd=" << dist.zd*360*60 << "' dAz=" << dist.az*360*60 << "'";
2615 T::Info(out);
2616
2617 fStep++;
2618 }
2619
2620 return T::GetCurrentState()>=State::kInitialized ?
2621 T::GetCurrentState() : State::kInitialized;
2622 }
2623
2624public:
2625 StateMachineDrive(ostream &out=cout) :
2626 StateMachineAsio<T>(out, "DRIVE_CONTROL"), fDrive(*this, *this),
2627 fTrackingLoop(*this), fSunRise(Time().GetNextSunRise()), fDevBuffer(5)
2628 {
2629
2630 T::Subscribe("MAGIC_WEATHER/DATA")
2631 (bind(&StateMachineDrive::HandleWeatherData, this, placeholders::_1));
2632
2633 T::Subscribe("TPOINT/DATA")
2634 (bind(&StateMachineDrive::HandleTPoint, this, placeholders::_1));
2635
2636 // State names
2637 T::AddStateName(State::kDisconnected, "Disconnected",
2638 "No connection to SPS");
2639 T::AddStateName(State::kConnected, "Connected",
2640 "Connection to SPS, no information received yet");
2641
2642 T::AddStateName(State::kLocked, "Locked",
2643 "Drive system is locked (will not accept commands)");
2644
2645 T::AddStateName(State::kUnavailable, "Unavailable",
2646 "Connected to SPS, no connection to at least one IndraDrives");
2647 T::AddStateName(State::kAvailable, "Available",
2648 "Connected to SPS and to IndraDrives, but at least one drive not in RF");
2649 T::AddStateName(State::kBlocked, "Blocked",
2650 "Drive system is blocked by manual operation or a pressed emergeny button");
2651 T::AddStateName(State::kArmed, "Armed",
2652 "Connected to SPS and IndraDrives in RF, but not yet initialized");
2653 T::AddStateName(State::kInitialized, "Initialized",
2654 "Connected to SPS and IndraDrives in RF and initialized");
2655
2656 T::AddStateName(State::kStopping, "Stopping",
2657 "Stop command sent, waiting for telescope to be still");
2658 T::AddStateName(State::kParking, "Parking",
2659 "Telescope in parking operation, waiting for telescope to be still");
2660 T::AddStateName(State::kMoving, "Moving",
2661 "Telescope moving");
2662 T::AddStateName(State::kTracking, "Tracking",
2663 "Telescope in tracking mode");
2664 T::AddStateName(State::kOnTrack, "OnTrack",
2665 "Telescope tracking stable");
2666
2667 T::AddStateName(State::kPositioningFailed, "PositioningFailed",
2668 "Target position was not reached within ten steps");
2669 T::AddStateName(State::kAllowedRangeExceeded, "OutOfRange",
2670 "Telecope went out of range during tracking");
2671
2672
2673 T::AddEvent("REQUEST_SDO", "S:3", State::kArmed)
2674 (bind(&StateMachineDrive::RequestSdo, this, placeholders::_1))
2675 ("Request an SDO from the drive"
2676 "|node[uint32]:Node identifier (1:az, 3:zd)"
2677 "|index[uint32]:SDO index"
2678 "|subindex[uint32]:SDO subindex");
2679
2680 T::AddEvent("SET_SDO", "S:3;X:1", State::kArmed)
2681 (bind(&StateMachineDrive::SendSdo, this, placeholders::_1))
2682 ("Request an SDO from the drive"
2683 "|node[uint32]:Node identifier (1:az, 3:zd)"
2684 "|index[uint32]:SDO index"
2685 "|subindex[uint32]:SDO subindex"
2686 "|value[uint64]:Value");
2687
2688 // Drive Commands
2689 T::AddEvent("MOVE_TO", "D:2", State::kInitialized) // ->ZDAZ
2690 (bind(&StateMachineDrive::MoveTo, this, placeholders::_1))
2691 ("Move the telescope to the given local sky coordinates"
2692 "|Zd[deg]:Zenith distance"
2693 "|Az[deg]:Azimuth");
2694
2695 T::AddEvent("TRACK", "D:2", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2696 (bind(&StateMachineDrive::Track, this, placeholders::_1))
2697 ("Move the telescope to the given sky coordinates and start tracking them"
2698 "|Ra[h]:Right ascension"
2699 "|Dec[deg]:Declination");
2700
2701 T::AddEvent("WOBBLE", "D:4", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2702 (bind(&StateMachineDrive::Wobble, this, placeholders::_1))
2703 ("Move the telescope to the given wobble position around the given sky coordinates and start tracking them"
2704 "|Ra[h]:Right ascension"
2705 "|Dec[deg]:Declination"
2706 "|Offset[deg]:Wobble offset"
2707 "|Angle[deg]:Wobble angle");
2708
2709 T::AddEvent("ORBIT", "D:5", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2710 (bind(&StateMachineDrive::Orbit, this, placeholders::_1))
2711 ("Move the telescope in a circle around the source"
2712 "|Ra[h]:Right ascension"
2713 "|Dec[deg]:Declination"
2714 "|Offset[deg]:Wobble offset"
2715 "|Angle[deg]:Starting angle"
2716 "|Period[min]:Time for one orbit");
2717
2718 T::AddEvent("TRACK_SOURCE", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2719 (bind(&StateMachineDrive::TrackSource, this, placeholders::_1))
2720 ("Move the telescope to the given wobble position around the given source and start tracking"
2721 "|Offset[deg]:Wobble offset"
2722 "|Angle[deg]:Wobble angle"
2723 "|Name[string]:Source name");
2724
2725 T::AddEvent("TRACK_WOBBLE", "S:1;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2726 (bind(&StateMachineDrive::TrackWobble, this, placeholders::_1))
2727 ("Move the telescope to the given wobble position around the given source and start tracking"
2728 "|Id:Wobble angle id (1 or 2)"
2729 "|Name[string]:Source name");
2730
2731 T::AddEvent("TRACK_ORBIT", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2732 (bind(&StateMachineDrive::TrackOrbit, this, placeholders::_1))
2733 ("Move the telescope in a circle around the source"
2734 "|Angle[deg]:Starting angle"
2735 "|Period[min]:Time for one orbit"
2736 "|Name[string]:Source name");
2737
2738 T::AddEvent("TRACK_ON", "C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2739 (bind(&StateMachineDrive::TrackOn, this, placeholders::_1))
2740 ("Move the telescope to the given position and start tracking"
2741 "|Name[string]:Source name");
2742
2743 T::AddEvent("MOON", State::kInitialized, State::kTracking, State::kOnTrack)
2744 (bind(&StateMachineDrive::TrackCelest, this, kEMoon))
2745 ("Start tracking the moon");
2746 T::AddEvent("VENUS", State::kInitialized, State::kTracking, State::kOnTrack)
2747 (bind(&StateMachineDrive::TrackCelest, this, kEVenus))
2748 ("Start tracking Venus");
2749 T::AddEvent("MARS", State::kInitialized, State::kTracking, State::kOnTrack)
2750 (bind(&StateMachineDrive::TrackCelest, this, kEMars))
2751 ("Start tracking Mars");
2752 T::AddEvent("JUPITER", State::kInitialized, State::kTracking, State::kOnTrack)
2753 (bind(&StateMachineDrive::TrackCelest, this, kEJupiter))
2754 ("Start tracking Jupiter");
2755 T::AddEvent("SATURN", State::kInitialized, State::kTracking, State::kOnTrack)
2756 (bind(&StateMachineDrive::TrackCelest, this, kESaturn))
2757 ("Start tracking Saturn");
2758
2759 // FIXME: What to do in error state?
2760 T::AddEvent("PARK", State::kInitialized, State::kMoving, State::kTracking, State::kOnTrack)
2761 (bind(&StateMachineDrive::Park, this))
2762 ("Park the telescope");
2763
2764 T::AddEvent("STOP")(State::kUnavailable)(State::kAvailable)(State::kArmed)(State::kInitialized)(State::kStopping)(State::kParking)(State::kMoving)(State::kTracking)(State::kOnTrack)(State::kPositioningFailed)(State::kAllowedRangeExceeded)
2765 (bind(&StateMachineDrive::StopMovement, this))
2766 ("Stop any kind of movement.");
2767
2768 T::AddEvent("TPOINT", State::kOnTrack)
2769 (bind(&StateMachineDrive::TPoint, this))
2770 ("Take a TPoint");
2771
2772 T::AddEvent("SET_LED_BRIGHTNESS", "I:2")
2773 (bind(&StateMachineDrive::SetLedBrightness, this, placeholders::_1))
2774 ("Set the LED brightness of the top and bottom leds"
2775 "|top[au]:Allowed range 0-32767 for top LEDs"
2776 "|bot[au]:Allowed range 0-32767 for bottom LEDs");
2777
2778 T::AddEvent("LEDS_OFF")
2779 (bind(&StateMachineDrive::SetLedsOff, this))
2780 ("Switch off TPoint LEDs");
2781
2782 T::AddEvent("UNLOCK", Drive::State::kLocked)
2783 (bind(&StateMachineDrive::Unlock, this))
2784 ("Unlock locked state.");
2785
2786 // Verbosity commands
2787 T::AddEvent("SET_VERBOSITY", "S:1")
2788 (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1))
2789 ("Set verbosity state"
2790 "|verbosity[uint16]:disable or enable verbosity for received data (yes/no), except dynamic data");
2791
2792 // Conenction commands
2793 T::AddEvent("DISCONNECT", State::kConnected)
2794 (bind(&StateMachineDrive::Disconnect, this))
2795 ("disconnect from ethernet");
2796
2797 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
2798 (bind(&StateMachineDrive::Reconnect, this, placeholders::_1))
2799 ("(Re)connect Ethernet connection to SPS, a new address can be given"
2800 "|[host][string]:new ethernet address in the form <host:port>");
2801
2802
2803 T::AddEvent("PRINT")
2804 (bind(&StateMachineDrive::Print, this))
2805 ("Print source list.");
2806
2807 T::AddEvent("RELOAD_SOURCES", State::kDisconnected, State::kConnected, State::kArmed, State::kInitialized, State::kLocked)
2808 (bind(&StateMachineDrive::ReloadSources, this))
2809 ("Reload sources from database after database has changed..");
2810
2811
2812 //fDrive.SetUpdateStatus(std::bind(&StateMachineDrive::UpdateStatus, this, placeholders::_1, placeholders::_2));
2813 fDrive.StartConnect();
2814 }
2815
2816 void SetEndpoint(const string &url)
2817 {
2818 fDrive.SetEndpoint(url);
2819 }
2820
2821 bool AddSource(const string &name, const Source &src)
2822 {
2823 const auto it = fSources.find(name);
2824 if (it!=fSources.end())
2825 T::Warn("Source '"+name+"' already in list... overwriting.");
2826
2827 fSources[name] = src;
2828 return it==fSources.end();
2829 }
2830
2831 void ReadDatabase(bool print=true)
2832 {
2833#ifdef HAVE_SQL
2834 Database db(fDatabase);
2835
2836 T::Message("Connected to '"+db.uri()+"'");
2837
2838 const mysqlpp::StoreQueryResult res =
2839 db.query("SELECT fSourceName, fRightAscension, fDeclination, fWobbleOffset, fWobbleAngle0, fWobbleAngle1, fMagnitude FROM Source").store();
2840
2841 fSources.clear();
2842 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2843 {
2844 const string name = (*v)[0].c_str();
2845
2846 Source src;
2847 src.name = name;
2848 src.ra = (*v)[1];
2849 src.dec = (*v)[2];
2850 src.offset = (*v)[3];
2851 src.angles[0] = (*v)[4];
2852 src.angles[1] = (*v)[5];
2853 src.mag = (*v)[6] ? double((*v)[6]) : 0;
2854 AddSource(name, src);
2855
2856 if (!print)
2857 continue;
2858
2859 ostringstream msg;
2860 msg << " " << name << setprecision(8) << ": Ra=" << src.ra << "h Dec=" << src.dec << "deg";
2861 msg << " Wobble=[" << src.offset << "," << src.angles[0] << "," << src.angles[1] << "] Mag=" << src.mag;
2862 T::Message(msg);
2863 }
2864#else
2865 T::Warn("MySQL support not compiled into the program.");
2866#endif
2867 }
2868
2869 int EvalOptions(Configuration &conf)
2870 {
2871 if (!fSunRise)
2872 return 1;
2873
2874 fDrive.SetVerbose(!conf.Get<bool>("quiet"));
2875
2876 fMaxPointingResidual = conf.Get<double>("pointing.max.residual");
2877 fPointingVelocity = conf.Get<double>("pointing.velocity");
2878
2879 fPointingMin = Encoder(conf.Get<double>("pointing.min.zd"),
2880 conf.Get<double>("pointing.min.az"));
2881 fPointingMax = Encoder(conf.Get<double>("pointing.max.zd"),
2882 conf.Get<double>("pointing.max.az"));
2883
2884 fParkingPos.zd = conf.Has("parking-pos.zd") ? conf.Get<double>("parking-pos.zd") : 90;
2885 fParkingPos.az = conf.Has("parking-pos.az") ? conf.Get<double>("parking-pos.az") : 0;
2886
2887 if (!CheckRange(fParkingPos))
2888 return 2;
2889
2890 fAccPointing = Acceleration(conf.Get<double>("pointing.acceleration.zd"),
2891 conf.Get<double>("pointing.acceleration.az"));
2892 fAccTracking = Acceleration(conf.Get<double>("tracking.acceleration.zd"),
2893 conf.Get<double>("tracking.acceleration.az"));
2894 fAccMax = Acceleration(conf.Get<double>("acceleration.max.zd"),
2895 conf.Get<double>("acceleration.max.az"));
2896
2897 fWeatherTimeout = conf.Get<uint16_t>("weather-timeout");
2898
2899 if (fAccPointing>fAccMax)
2900 {
2901 T::Error("Pointing acceleration exceeds maximum acceleration.");
2902 return 3;
2903 }
2904
2905 if (fAccTracking>fAccMax)
2906 {
2907 T::Error("Tracking acceleration exceeds maximum acceleration.");
2908 return 4;
2909 }
2910
2911 fDeviationLimit = conf.Get<uint16_t>("deviation-limit");
2912 fDeviationCounter = conf.Get<uint16_t>("deviation-count");
2913 fDeviationMax = conf.Get<uint16_t>("deviation-max");
2914
2915 const string fname = conf.Get<string>("pointing.model-file");
2916
2917 try
2918 {
2919 fPointingModel.Load(fname);
2920 }
2921 catch (const exception &e)
2922 {
2923 T::Error(e.what());
2924 return 5;
2925 }
2926
2927 const vector<string> &vec = conf.Vec<string>("source");
2928
2929 for (vector<string>::const_iterator it=vec.begin(); it!=vec.end(); it++)
2930 {
2931 istringstream stream(*it);
2932
2933 string name;
2934
2935 int i=0;
2936
2937 Source src;
2938
2939 string buffer;
2940 while (getline(stream, buffer, ','))
2941 {
2942 istringstream is(buffer);
2943
2944 switch (i++)
2945 {
2946 case 0: name = buffer; break;
2947 case 1: src.ra = ReadAngle(is); break;
2948 case 2: src.dec = ReadAngle(is); break;
2949 case 3: is >> src.offset; break;
2950 case 4: is >> src.angles[0]; break;
2951 case 5: is >> src.angles[1]; break;
2952 }
2953
2954 if (is.fail())
2955 break;
2956 }
2957
2958 if (i==3 || i==6)
2959 {
2960 AddSource(name, src);
2961 continue;
2962 }
2963
2964 T::Warn("Resource 'source' not correctly formatted: '"+*it+"'");
2965 }
2966
2967 //fAutoResume = conf.Get<bool>("auto-resume");
2968
2969 if (conf.Has("source-database"))
2970 {
2971 fDatabase = conf.Get<string>("source-database");
2972 ReadDatabase();
2973 }
2974
2975 if (fSunRise.IsValid())
2976 {
2977 ostringstream msg;
2978 msg << "Next sun-rise will be at " << fSunRise;
2979 T::Message(msg);
2980 }
2981
2982 // The possibility to connect should be last, so that
2983 // everything else is already initialized.
2984 SetEndpoint(conf.Get<string>("addr"));
2985
2986 return -1;
2987 }
2988};
2989
2990// ------------------------------------------------------------------------
2991
2992#include "Main.h"
2993
2994
2995template<class T, class S, class R>
2996int RunShell(Configuration &conf)
2997{
2998 return Main::execute<T, StateMachineDrive<S, R>>(conf);
2999}
3000
3001void SetupConfiguration(Configuration &conf)
3002{
3003 po::options_description control("Drive control options");
3004 control.add_options()
3005 ("quiet,q", po_bool(), "Disable debug messages")
3006 ("no-dim,d", po_switch(), "Disable dim services")
3007 ("addr,a", var<string>("sps:5357"), "Network address of cosy")
3008 ("verbosity,v", var<uint16_t>(0), "Vervosity level (0=off; 1=major updates; 2=most updates; 3=frequent updates)")
3009 ("pointing.model-file", var<string>()->required(), "Name of the file with the pointing model in use")
3010 ("pointing.max.zd", var<double>( 104.9), "Maximum allowed zenith angle in sky pointing coordinates [deg]")
3011 ("pointing.max.az", var<double>( 85.0), "Maximum allowed azimuth angle in sky pointing coordinates [deg]")
3012 ("pointing.min.zd", var<double>(-104.9), "Minimum allowed zenith angle in sky pointing coordinates [deg]")
3013 ("pointing.min.az", var<double>(-295.0), "Minimum allowed azimuth angle in sky pointing coordinates [deg]")
3014 ("pointing.max.residual", var<double>(1./32768), "Maximum residual for a pointing operation [revolutions]")
3015 ("pointing.velocity", var<double>(0.3), "Moving velocity when pointing [% max]")
3016 ("pointing.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis for pointing operations")
3017 ("pointing.acceleration.zd", var<double>(0.03), "Acceleration for zenith axis for pointing operations")
3018 ("tracking.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis during tracking operations")
3019 ("tracking.acceleration.zd", var<double>(0.01), "Acceleration for zenith axis during tracking operations")
3020 ("parking-pos.zd", var<double>(101), "Parking position zenith angle in sky pointing coordinates [deg]")
3021 ("parking-pos.az", var<double>(0), "Parking position azimuth angle in sky pointing coordinates [deg]")
3022 ("acceleration.max.az", var<double>(0.03), "Maximum allowed acceleration value for azimuth axis")
3023 ("acceleration.max.zd", var<double>(0.09), "Maximum allowed acceleration value for zenith axis")
3024 ("weather-timeout", var<uint16_t>(300), "Timeout [sec] for weather data (after timeout default values are used)")
3025 ("deviation-limit", var<uint16_t>(90), "Deviation limit in arcsec to get 'OnTrack'")
3026 ("deviation-count", var<uint16_t>(3), "Minimum number of reported deviation below deviation-limit to get 'OnTrack'")
3027 ("deviation-max", var<uint16_t>(180), "Maximum deviation in arcsec allowed to keep status 'OnTrack'")
3028 ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
3029 ("source", vars<string>(), "Additional source entry in the form \"name,hh:mm:ss,dd:mm:ss\"")
3030 ;
3031
3032 conf.AddOptions(control);
3033}
3034
3035/*
3036 Extract usage clause(s) [if any] for SYNOPSIS.
3037 Translators: "Usage" and "or" here are patterns (regular expressions) which
3038 are used to match the usage synopsis in program output. An example from cp
3039 (GNU coreutils) which contains both strings:
3040 Usage: cp [OPTION]... [-T] SOURCE DEST
3041 or: cp [OPTION]... SOURCE... DIRECTORY
3042 or: cp [OPTION]... -t DIRECTORY SOURCE...
3043 */
3044void PrintUsage()
3045{
3046 cout <<
3047 "The drivectrl is an interface to the drive PLC.\n"
3048 "\n"
3049 "The default is that the program is started without user intercation. "
3050 "All actions are supposed to arrive as DimCommands. Using the -c "
3051 "option, a local shell can be initialized. With h or help a short "
3052 "help message about the usuage can be brought to the screen.\n"
3053 "\n"
3054 "Usage: drivectrl [-c type] [OPTIONS]\n"
3055 " or: drivectrl [OPTIONS]\n";
3056 cout << endl;
3057}
3058
3059void PrintHelp()
3060{
3061 Main::PrintHelp<StateMachineDrive<StateMachine,ConnectionDrive>>();
3062
3063 /* Additional help text which is printed after the configuration
3064 options goes here */
3065
3066 /*
3067 cout << "bla bla bla" << endl << endl;
3068 cout << endl;
3069 cout << "Environment:" << endl;
3070 cout << "environment" << endl;
3071 cout << endl;
3072 cout << "Examples:" << endl;
3073 cout << "test exam" << endl;
3074 cout << endl;
3075 cout << "Files:" << endl;
3076 cout << "files" << endl;
3077 cout << endl;
3078 */
3079}
3080
3081int main(int argc, const char* argv[])
3082{
3083 Configuration conf(argv[0]);
3084 conf.SetPrintUsage(PrintUsage);
3085 Main::SetupConfiguration(conf);
3086 SetupConfiguration(conf);
3087
3088 if (!conf.DoParse(argc, argv, PrintHelp))
3089 return 127;
3090
3091 //try
3092 {
3093 // No console access at all
3094 if (!conf.Has("console"))
3095 {
3096 if (conf.Get<bool>("no-dim"))
3097 return RunShell<LocalStream, StateMachine, ConnectionDrive>(conf);
3098 else
3099 return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(conf);
3100 }
3101 // Cosole access w/ and w/o Dim
3102 if (conf.Get<bool>("no-dim"))
3103 {
3104 if (conf.Get<int>("console")==0)
3105 return RunShell<LocalShell, StateMachine, ConnectionDrive>(conf);
3106 else
3107 return RunShell<LocalConsole, StateMachine, ConnectionDrive>(conf);
3108 }
3109 else
3110 {
3111 if (conf.Get<int>("console")==0)
3112 return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
3113 else
3114 return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
3115 }
3116 }
3117 /*catch (std::exception& e)
3118 {
3119 cerr << "Exception: " << e.what() << endl;
3120 return -1;
3121 }*/
3122
3123 return 0;
3124}
Note: See TracBrowser for help on using the repository browser.