source: trunk/FACT++/scripts/Main.js@ 15325

Last change on this file since 15325 was 15293, checked in by tbretz, 12 years ago
Added a check for schedule-database
File size: 36.5 KB
Line 
1/**
2 * @fileOverview This file has functions related to documenting JavaScript.
3 * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
4 */
5'use strict';
6
7dim.log("Start: "+__FILE__+" ["+__DATE__+"]");
8
9// This should be set in dimctrl.rc as JavaScript.schedule-database.
10// It is sent together with the script to the dimserver.
11// If started directly, it has to be set after the command:
12//
13// .js scripts/Main.js schedule-database=...
14//
15if (!$['schedule-database'])
16 throw new Error("Environment 'schedule-database' not set!");
17
18//dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
19
20// ================================================================
21// Code related to the schedule
22// ================================================================
23
24//this is just the class implementation of 'Observation'
25include('scripts/Observation_class.js');
26
27/*
28// this file just contains the definition of
29// the variable observations, which builds our nightly schedule, hence the filename
30include('scripts/schedule.js');
31
32// make Observation objects from user input and check if 'date' is increasing.
33for (var i=0; i<observations.length; i++)
34{
35 observations[i] = new Observation(observations[i]);
36
37 // check if the start date given by the user is increasing.
38 if (i>0 && observations[i].start <= observations[i-1].start)
39 {
40 throw new Error("Start time '"+ observations[i].start.toUTCString()+
41 "' in row "+i+" exceeds start time in row "+(i-1));
42 }
43}
44*/
45
46include('scripts/getSchedule.js');
47
48var observations = [ ];
49
50// Get the observation scheduled for 'now' from the table and
51// return its index
52function getObservation(now)
53{
54 if (now==undefined)
55 now = new Date();
56
57 if (isNaN(now.valueOf()))
58 throw new Error("Date argument in getObservation invalid.");
59
60 observations = getSchedule();
61
62 for (var i=0; i<observations.length; i++)
63 if (now<observations[i].start)
64 return i-1;
65
66 return observations.length-1;
67}
68
69// ================================================================
70// Code to check whether observation is allowed
71// ================================================================
72/*
73function currentEst(source)
74{
75 var moon = new Moon();
76 if (!moon.isUp)
77 return 7.7;
78
79 var dist = Sky.dist(moon, source);
80
81 var alt = 90-moon.toLocal().zd;
82
83 var lc = dist*alt*pow(Moon.disk(), 6)/360/360;
84
85 var cur = 7.7+4942*lc;
86
87 return cur;
88}
89
90function thresholdEst(source) // relative threshold (ratio)
91{
92 // Assumption:
93 // atmosphere is 70km, shower taks place after 60km, earth radius 6400km
94 // just using the cosine law
95 // This fits very well with MC results: See Roger Firpo, p.45
96 // "Study of the MAGIC telescope sensitivity for Large Zenith Angle observations"
97
98 var c = Math.cos(Math.Pi-source.zd);
99 var ratio = (10*sqrt(409600*c*c+9009) + 6400*c - 60)/10;
100
101 // assumption: Energy threshold increases linearily with current
102 // assumption: Energy threshold increases linearily with distance
103
104 return ratio*currentEst(source)/7.7;
105}
106*/
107
108// ----------------------------------------------------------------
109
110// ================================================================
111// Code related to monitoring the fad system
112// ================================================================
113
114var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
115
116var incomplete = 0;
117
118sub_incomplete.onchange = function(evt)
119{
120 if (!evt.data)
121 {
122 this.last_inc = 0;
123 return;
124 }
125
126 var inc = evt.obj['incomplete'];
127 if (!inc)
128 {
129 this.last_inc = 0;
130 return;
131 }
132
133 if (this.last_inc==inc)
134 return;
135
136 incomplete = inc;
137
138 var reset = [ ];
139
140 for (var i=0; i<40; i++)
141 if (inc&(1<<i))
142 {
143 console.out(" FAD %2d".$(i)+" lost during data taking.");
144 reset.push(parseInt(i/10));
145 }
146
147 if (reset.length>10)
148 return;
149
150 dim.send("MCP/RESET");
151
152 reset = reset.filter(function(elem,pos){return reset.indexOf(elem)==pos;});
153
154 if (reset.length>0)
155 {
156 console.out("");
157 console.out(" FADs belong to crate(s): "+reset);
158 console.out("");
159 }
160
161 this.last_inc = inc;
162}
163
164var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
165
166/**
167 * call-back function of FAD_CONTROL/CONNECTIONS
168 * store IDs of problematic FADs
169 *
170 */
171/*
172sub_connections.onchange = function(evt)
173{
174 // This happens, but why?
175 if (!evt.obj['status'])
176 return;
177
178 this.reset = [ ];
179
180 for (var x=0; x<40; x++)
181 if (evt.obj['status'][x]!=66 && evt.obj['status'][x]!=67)
182 this.reset.push(x);
183
184 if (this.reset.length==0)
185 return;
186
187 //m.alarm("FAD board loss detected...");
188 dim.send("MCP/RESET");
189 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
190}
191*/
192
193/**
194 * reconnect to problematic FADs
195 *
196 * Dis- and Reconnects to FADs, found to be problematic by call-back function
197 * onchange() to have a different CONNECTION value than 66 or 67.
198 *
199 * @returns
200 * a boolean is returned.
201 * reconnect returns true if:
202 * * nothing needed to be reset --> no problems found by onchange()
203 * * the reconnection went fine.
204 *
205 * reconnect *never returns false* so far.
206 *
207 * @example
208 * if (!sub_connections.reconnect())
209 * exit();
210 */
211sub_connections.reconnect = function()
212{
213 // this.reset is a list containing the IDs of FADs,
214 // which have neither CONNECTION==66 nor ==67, whatever this means :-)
215 if (this.reset.length==0)
216 return true;
217
218 console.out(" Reconnect: start ["+this.reset.length+"]");
219
220 for (var i=0; i<this.reset.length; i++)
221 dim.send("FAD_CONTROL/DISCONNECT", this.reset[i]);
222
223 v8.sleep(3000);
224
225 while (this.reset.length)
226 dim.send("FAD_CONTROL/CONNECT", this.reset.pop());
227
228 v8.sleep(1000);
229 dim.wait("FAD_CONTROL", "Connected", 3000);
230
231 console.out(" Reconnect: end");
232
233 return true;
234}
235
236// ================================================================
237// Code related to taking data
238// ================================================================
239
240var startrun = new Subscription("FAD_CONTROL/START_RUN");
241startrun.get(5000);
242
243function takeRun(type, count, time)
244{
245 if (!count)
246 count = -1;
247 if (!time)
248 time = -1;
249
250 var nextrun = startrun.get().obj['next'];
251 console.out(" Take run %3d".$(nextrun)+": N="+count+" T="+time+"s ["+type+"]");
252
253 dim.send("MCP/START", time?time:-1, count?count:-1, type);
254
255 // FIXME: Replace by callback?
256 //
257 // DN: I believe instead of waiting for 'TakingData' one could split this
258 // up into two checks with an extra condition:
259 // if type == 'data':
260 // wait until ThresholdCalibration starts:
261 // --> this time should be pretty identical for each run
262 // if this takes longer than say 3s:
263 // there might be a problem with one/more FADs
264 //
265 // wait until "TakingData":
266 // --> this seems to take even some minutes sometimes...
267 // (might be optimized rather soon, but still in the moment...)
268 // if this takes way too long:
269 // there might be something broken,
270 // so maybe a very high time limit is ok here.
271 // I think there is not much that can go wrong,
272 // when the Thr-Calib has already started. Still it might be nice
273 // If in the future RateControl is written so to find out that
274 // in case the threshold finding algorithm does
275 // *not converge as usual*
276 // it can complain, and in this way give a hint, that the weather
277 // might be a little bit too bad.
278 // else:
279 // wait until "TakingData":
280 // --> in a non-data run this time should be pretty short again
281 // if this takes longer than say 3s:
282 // there might be a problem with one/more FADs
283 //
284
285 // Use this if you use the rate control to calibrate by rates
286 //if (!dim.wait("MCP", "TakingData", -300000) )
287 //{
288 // throw new Error("MCP took longer than 5 minutes to start TakingData"+
289 // "maybe this idicates a problem with one of the FADs?");
290 //}
291
292 // Here we could check and handle fad losses
293 try
294 {
295 dim.wait("MCP", "TakingData", 15000);
296 }
297 catch (e)
298 {
299 console.out("");
300 console.out("MCP: "+dim.state("MCP").name);
301 console.out("FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
302 console.out("FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
303 console.out("");
304
305 if (dim.state("MCP").name!="Configuring3" ||
306 dim.state("FAD_CONTROL").name!="Configuring2")
307 throw e;
308
309 console.out("");
310 console.out("Waiting for fadctrl to get configured timed out... checking for in-run FAD loss.");
311
312 var con = sub_connections.get();
313 var stat = con.obj['status'];
314
315 console.out("Sending MCP/RESET");
316 dim.send("MCP/RESET");
317
318 dim.wait("FTM_CONTROL", "Idle", 3000);
319 dim.wait("FAD_CONTROL", "Connected", 3000);
320 dim.wait("MCP", "Idle", 3000);
321
322 var reset = [ ];
323
324 for (var i=0; i<40; i++)
325 if (stat[i]!=0x43)
326 {
327 console.out(" FAD %2d".$(i)+" not in Configured state.");
328 reset.push(parseInt(i/10));
329 }
330
331 reset = reset.filter(function(elem,pos){return reset.indexOf(elem)==pos;});
332
333 if (reset.length>0)
334 {
335 console.out("");
336 console.out(" FADs belong to crate(s): "+reset);
337 console.out("");
338 }
339
340 console.out("");
341 console.out("Trying automatic reconnect...");
342
343 for (var i=0; i<40; i++)
344 if (stat[i]!=0x43)
345 {
346 console.out(" ...disconnect "+i);
347 dim.send("FAD_CONTROL/DISCONNECT", i);
348 }
349
350 console.out(" ...waiting for 5s");
351 v8.sleep(5000);
352
353 for (var i=0; i<40; i++)
354 if (stat[i]!=0x43)
355 {
356 console.out(" ...reconnect "+i);
357 dim.send("FAD_CONTROL/CONNECT", i);
358 }
359
360 console.out(" ...waiting for 1s");
361 v8.sleep(1000);
362 console.out("");
363
364 throw e;
365 }
366
367 dim.wait("MCP", "Idle", time>0 ? time*1250 : undefined); // run time plus 25%
368
369 console.out("debug: "+incomplete);
370
371 if (incomplete)
372 {
373 console.out("");
374 console.out("Trying automatic reconnect...");
375
376 for (var i=0; i<40; i++)
377 if (incomplete&(1<<i))
378 {
379 console.out(" ...disconnect "+i);
380 dim.send("FAD_CONTROL/DISCONNECT", i);
381 }
382
383 console.out(" ...waiting for 5s");
384 v8.sleep(5000);
385
386 for (var i=0; i<40; i++)
387 if (incomplete&(1<<i))
388 {
389 console.out(" ...reconnect "+i);
390 dim.send("FAD_CONTROL/CONNECT", i);
391 }
392
393 console.out(" ...waiting for 1s");
394 v8.sleep(1000);
395 console.out("");
396
397 incomplete = 0;
398
399 throw new Error("In-run FAD loss detected.");
400 }
401
402 //console.out(" Take run: end");
403
404 // DN: currently reconnect() never returns false
405 // .. but it can fail of course.
406 //if (!sub_connections.reconnect())
407 // exit();
408
409 return true;//sub_connections.reconnect();
410}
411
412// ----------------------------------------------------------------
413
414function doDrsCalibration(where)
415{
416 console.out(" Take DRS calibration ["+where+"]");
417
418 service_feedback.voltageOff();
419
420 var tm = new Date();
421
422 while (1)
423 {
424 dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
425 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
426 continue;
427 if (!takeRun("drs-gain", 1000)) // 40 / 20s (50Hz)
428 continue;
429 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
430 continue;
431
432 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
433 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
434 continue;
435 if (!takeRun("drs-time", 1000)) // 40 / 20s (50Hz)
436 continue;
437
438 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
439 if (!takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
440 continue;
441
442 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
443 if (!takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
444 continue;
445 // -----------
446 // 4'40 / 2'00
447
448 break;
449 }
450
451 console.out(" DRS calibration done [%.1f]".$((new Date()-tm)/1000));
452}
453
454// ================================================================
455// Code related to the lid
456// ================================================================
457
458function OpenLid()
459{
460 /*
461 while (Sun.horizon(-13).isUp)
462 {
463 var now = new Date();
464 var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
465 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
466 v8.sleep(60000);
467 }*/
468
469 var isClosed = dim.state("LID_CONTROL").name=="Closed";
470
471 var tm = new Date();
472
473 // Wait for lid to be open
474 if (isClosed)
475 {
476 console.out(" Open lid: start");
477 dim.send("LID_CONTROL/OPEN");
478 }
479 dim.wait("LID_CONTROL", "Open", 30000);
480
481 if (isClosed)
482 console.out(" Open lid: done [%.1fs]".$((new Date()-tm)/1000));
483}
484
485function CloseLid()
486{
487 var isOpen = dim.state("LID_CONTROL").name=="Open";
488
489 var tm = new Date();
490
491 // Wait for lid to be open
492 if (isOpen)
493 {
494 console.out(" Close lid: start");
495 dim.send("LID_CONTROL/CLOSE");
496 }
497 dim.wait("LID_CONTROL", "Closed", 30000);
498
499 if (isOpen)
500 console.out(" Close lid: end [%.1fs]".$((new Date()-tm)/1000));
501}
502
503// ================================================================
504// Code related to switching bias voltage on and off
505// ================================================================
506
507var service_feedback = new Subscription("FEEDBACK/DEVIATION");
508
509service_feedback.onchange = function(evt)
510{
511 if (this.cnt && evt.counter>this.cnt+12)
512 return;
513
514 this.voltageStep = null;
515 if (!evt.obj)
516 return;
517
518 var delta = evt.obj['DeltaBias'];
519
520 var avg = 0;
521 for (var i=0; i<320; i++)
522 avg += delta[i];
523 avg /= 320;
524
525 if (this.previous)
526 this.voltageStep = Math.abs(avg-this.previous);
527
528 this.previous = avg;
529
530 console.out(" DeltaV="+this.voltageStep);
531}
532
533// DN: Why is voltageOff() implemented as
534// a method of a Subscription to a specific Service
535// I naively would think of voltageOff() as an unbound function.
536// I seems to me it has to be a method of a Subscription object, in order
537// to use the update counting method. But does it have to be
538// a Subscription to FEEDBACK/DEVIATION, or could it work with other services as well?
539service_feedback.voltageOff = function()
540{
541 var state = dim.state("BIAS_CONTROL").name;
542
543 // check of feedback has to be switched on
544 var isOn = state=="VoltageOn" || state=="Ramping";
545 if (isOn)
546 {
547 console.out(" Voltage off: start");
548
549 // Supress the possibility that the bias control is
550 // ramping and will reject the command to switch the
551 // voltage off
552 var isControl = dim.state("FEEDBACK").name=="CurrentControl";
553 if (isControl)
554 {
555 console.out(" Suspending feedback.");
556 dim.send("FEEDBACK/ENABLE_OUTPUT", false);
557 dim.wait("FEEDBACK", "CurrentCtrlIdle", 3000);
558 }
559
560 // Switch voltage off
561 console.out(" Voltage on: switch off");
562 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
563
564 // If the feedback was enabled, re-enable it
565 if (isControl)
566 {
567 console.out(" Resuming feedback.");
568 dim.send("FEEDBACK/ENABLE_OUTPUT", true);
569 dim.wait("FEEDBACK", "CurrentControl", 3000);
570 }
571 }
572
573 dim.wait("BIAS_CONTROL", "VoltageOff", 5000);
574
575 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
576 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
577
578 if (isOn)
579 console.out(" Voltage off: end");
580}
581
582// DN: The name of the method voltageOn() in the context of the method
583// voltageOff() is a little bit misleading, since when voltageOff() returns
584// the caller can be sure the voltage is off, but when voltageOn() return
585// this is not the case, in the sense, that the caller can now take data.
586// instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
587// in order to safely take good-quality data.
588// This could lead to nasty bugs in the sense, that the second call might
589// be forgotten by somebody
590//
591// so I suggest to rename voltageOn() --> prepareVoltageOn()
592// waitForVoltageOn() stays as it is
593// and one creates a third method called:voltageOn() like this
594/* service_feedback.voltageOn = function()
595 * {
596 * this.prepareVoltageOn();
597 * this.waitForVoltageOn();
598 * }
599 *
600 * */
601// For convenience.
602
603service_feedback.voltageOn = function()
604{
605 //if (Sun.horizon("FACT").isUp)
606 // throw new Error("Sun is above FACT-horizon, voltage cannot be switched on.");
607
608 var isOff = dim.state("BIAS_CONTROL").name=="VoltageOff";
609 if (isOff)
610 {
611 console.out(" Voltage on: switch on");
612 //console.out(JSON.stringify(dim.state("BIAS_CONTROL")));
613
614 dim.send("BIAS_CONTROL/SET_GLOBAL_DAC", 1);
615 }
616
617 // Wait until voltage on
618 dim.wait("BIAS_CONTROL", "VoltageOn", 5000);
619
620 // From now on the feedback waits for a valid report from the FSC
621 // and than switchs to CurrentControl
622 dim.wait("FEEDBACK", "CurrentControl", 60000);
623
624 if (isOff)
625 {
626 console.out(" Voltage on: cnt="+this.cnt);
627
628 this.previous = undefined;
629 this.cnt = this.get().counter;
630 this.voltageStep = undefined;
631 }
632}
633
634service_feedback.waitForVoltageOn = function()
635{
636 // waiting 45sec for the current control to stabilize...
637 // v8.sleep(45000);
638
639 // ----- Wait for at least three updates -----
640 // The feedback is started as if the camera where at 0deg
641 // Then after the first temp update, the temperature will be set to the
642 // correct value (this has already happened)
643 // So we only have to wait for the current to get stable.
644 // This should happen after three to five current updates.
645 // So we want one recent temperature update
646 // and three recent current updates
647
648 // Avoid output if condition is already fulfilled
649 if (this.cnt && this.get().counter>this.cnt+10)
650 return;
651
652 // FIXME: timeout missing
653 console.out(" Feedback wait: start");
654
655 function func(service)
656 {
657 if ((service.cnt!=undefined && service.get().counter>service.cnt+10) ||
658 (service.voltageStep && service.voltageStep<0.02))
659 return true;
660 }
661
662 var now = new Date();
663 //v8.timeout(5*60000, func, this);
664 while ((this.cnt==undefined || this.get().counter<=this.cnt+10) && (!this.voltageStep || this.voltageStep>0.02))
665 v8.sleep();
666
667 console.out(" Feedback wait: end [dV=%.3f, cnt=%d, %.2fs]".$(this.voltageStep, this.get().counter, (new Date()-now)/1000));
668}
669
670// ================================================================
671// Function to shutdown the system
672// ================================================================
673
674function Shutdown()
675{
676 console.out("Shutdown: start");
677
678 service_feedback.voltageOff();
679 CloseLid();
680 dim.send("DRIVE_CONTROL/PARK");
681
682 console.out("Waiting for telescope to park. This may take a while.");
683
684 // FIXME: This might not work is the drive is already close to park position
685 dim.wait("DRIVE_CONTROL", "Locked", 3000);
686
687 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
688 sub.get(5000); // FIXME: Proper error message in case of failure
689
690 function func()
691 {
692 var report = sub.get();
693
694 var zd = report.obj['Zd'];
695 var az = report.obj['Az'];
696
697 if (zd>100 && Math.abs(az)<1)
698 return true;
699
700 return undefined;
701 }
702
703 var now = new Date();
704 v8.timeout(150000, func);
705
706 //dim.send("FEEDBACK/STOP");
707 dim.send("FEEDBACK/ENABLE_OUTPUT", false);
708 dim.send("FTM_CONTROL/STOP_TRIGGER");
709
710 dim.wait("FEEDBACK", "CurrentCtrlIdle", 3000);
711 dim.wait("FTM_CONTROL", "Idle", 3000);
712
713 var report = sub.get();
714
715 console.out("");
716 console.out("Shutdown procedure seems to be finished...");
717 console.out(" Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
718 console.out(" Please make sure the park position was reached");
719 console.out(" and the telescope is not moving anymore.");
720 console.out(" Please check that the lid is closed and the voltage switched off.");
721 console.out("");
722 console.out("Shutdown: end ["+(new Date()-now)/1000+"s]");
723
724 sub.close();
725}
726
727// ================================================================
728// Crosscheck all states
729// ================================================================
730
731// ----------------------------------------------------------------
732// Do a standard startup to bring the system in into a well
733// defined state
734// ----------------------------------------------------------------
735include('scripts/Startup.js');
736
737// ----------------------------------------------------------------
738// Check that everything we need is availabel to receive commands
739// (FIXME: Should that go to the general CheckState?)
740// ----------------------------------------------------------------
741console.out("Checking send.");
742checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
743console.out("Checking send: done");
744
745// ----------------------------------------------------------------
746// Bring feedback into the correct operational state
747// ----------------------------------------------------------------
748console.out("Feedback init: start.");
749service_feedback.get(5000);
750
751dim.send("FEEDBACK/ENABLE_OUTPUT", true);
752dim.send("FEEDBACK/START_CURRENT_CONTROL", 0.);
753
754v8.timeout(3000, function() { var n = dim.state("FEEDBACK").name; if (n=="CurrentCtrlIdle" || n=="CurrentControl") return true; });
755
756// ----------------------------------------------------------------
757// Connect to the DRS_RUNS service
758// ----------------------------------------------------------------
759console.out("Drs runs init: start.");
760
761var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
762sub_drsruns.get(5000);
763// FIXME: Check if the last DRS calibration was complete?
764
765function getTimeSinceLastDrsCalib()
766{
767 // ----- Time since last DRS Calibration [min] ------
768 var runs = sub_drsruns.get(0);
769 var diff = (new Date()-runs.time)/60000;
770
771 // Warning: 'roi=300' is a number which is not intrisically fixed
772 // but can change depending on the taste of the observers
773 var valid = runs.obj['run'][2]>0 && runs.obj['roi']==300;
774
775 if (valid)
776 console.out(" Last DRS calib: %.1fmin ago".$(diff));
777 else
778 console.out(" No valid drs calibration available");
779
780 return valid ? diff : null;
781}
782
783// ----------------------------------------------------------------
784// Make sure we will write files
785// ----------------------------------------------------------------
786dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
787
788// ----------------------------------------------------------------
789// Print some information for the user about the
790// expected first oberservation
791// ----------------------------------------------------------------
792var test = getObservation();
793if (test!=undefined)
794{
795 var n = new Date();
796 if (test==-1)
797 console.out(n.toUTCString()+": First observation scheduled for "+observations[0].start.toUTCString());
798 if (test>=0 && test<observations.length)
799 console.out(n.toUTCString()+": First observation should start immediately.");
800 if (observations[0].start>n+12*3600*1000)
801 console.out(n.toUTCString()+": No observations scheduled for the next 12 hours!");
802}
803
804// ----------------------------------------------------------------
805// Start main loop
806// ----------------------------------------------------------------
807console.out("Start main loop.");
808
809var run = -2; // getObservation never called
810var sub;
811var lastId;
812var sun = Sun.horizon(-13);
813var system_on; // undefined
814
815while (1)
816{
817 // Check if observation position is still valid
818 // If source position has changed, set run=0
819 var idxObs = getObservation();
820 if (idxObs===undefined)
821 break;
822
823 // we are still waiting for the first observation in the schedule
824 if (idxObs==-1)
825 {
826 // flag that the first observation will be in the future
827 run = -1;
828 v8.sleep(1000);
829 continue;
830 }
831
832 // Check if we have to take action do to sun-rise
833 var was_up = sun.isUp;
834 sun = Sun.horizon(-13);
835 if (!was_up && sun.isUp)
836 {
837 console.out("Sun rise detected.... automatic shutdown initiated!");
838 // FIXME: State check?
839 Shutdown();
840 system_on = false;
841 continue;
842 }
843
844 // Current and next observation target
845 var obs = observations[idxObs];
846 var nextObs = observations[idxObs+1];
847
848 // Check if observation target has changed
849 if (lastId!=obs.id) // !Object.isEqual(obs, nextObs)
850 {
851 console.out("--- "+obs.id+" ---");
852 console.out("Current time: "+new Date().toUTCString());
853 console.out("Current observation: "+obs.start.toUTCString());
854 if (nextObs!=undefined)
855 console.out("Next observation: "+nextObs.start.toUTCString());
856 console.out("");
857
858 // This is the first source, but we do not come from
859 // a scheduled 'START', so we have to check if the
860 // telescop is operational already
861 sub = 0;
862 if (run<0)
863 {
864 //Startup(); // -> Bias On/Off?, Lid open/closed?
865 //CloseLid();
866 }
867
868 // The first observation had a start-time in the past...
869 // In this particular case start with the last entry
870 // in the list of measurements
871 if (run==-2)
872 sub = obs.length-1;
873
874 run = 0;
875 }
876 lastId = obs.id;
877
878 if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
879 throw Error("Last scheduled measurement must be a shutdown.");
880
881 // We are done with all measurement slots for this
882 // observation... wait for next observation
883 if (sub>=obs.length)
884 {
885 v8.sleep(1000);
886 continue;
887 }
888
889 // Check if sun is still up... only DATA and RATESCAN must be suppressed
890 if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN") && sun.isUp)
891 {
892 var now = new Date();
893 var remaining = (sun.set - now)/60000;
894 console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
895 v8.sleep(60000);
896 continue;
897 }
898
899 if (system_on===false && task!="STARTUP")
900 {
901 v8.sleep(1000);
902 continue;
903 }
904
905 var power_states = sun.isUp || system_on===false ? [ "DriveOff" ] : [ "SystemOn" ];
906
907 var drive_states = sun.isUp || system_on===false ? [ ] : [ "Armed", "Tracking", "OnTrack" ];
908
909 // A scheduled task was found, lets check if all servers are
910 // still only and in reasonable states. If this is not the case,
911 // something unexpected must have happend and the script is aborted.
912 //console.out(" Checking states [general]");
913 var table =
914 [
915 [ "TNG_WEATHER" ],
916 [ "MAGIC_WEATHER" ],
917 [ "CHAT" ],
918 [ "SMART_FACT" ],
919 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
920 [ "FSC_CONTROL", [ "Connected" ] ],
921 [ "MCP", [ "Idle" ] ],
922 [ "TIME_CHECK", [ "Valid" ] ],
923 [ "PWR_CONTROL", power_states/*[ "SystemOn" ]*/ ],
924 [ "AGILENT_CONTROL", [ "VoltageOn" ] ],
925 [ "BIAS_CONTROL", [ "VoltageOff", "VoltageOn", "Ramping" ] ],
926 [ "FEEDBACK", [ "CurrentControl", "CurrentCtrlIdle" ] ],
927 [ "LID_CONTROL", [ "Open", "Closed" ] ],
928 [ "DRIVE_CONTROL", drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
929 [ "FTM_CONTROL", [ "Idle", "TriggerOn" ] ],
930 [ "FAD_CONTROL", [ "Connected", "WritingData" ] ],
931 [ "RATE_SCAN", [ "Connected" ] ],
932 [ "RATE_CONTROL", [ "Connected", "GlobalThresholdSet", "InProgress" ] ],
933 ];
934
935
936 if (!checkStates(table))
937 {
938 throw new Error("Something unexpected has happened. One of the servers"+
939 "is in a state in which it should not be. Please,"+
940 "try to find out what happened...");
941 }
942
943 console.out("\n"+(new Date()).toUTCString()+": Current measurement: "+obs[sub]);
944
945 // Check if obs.task is one of the one-time-tasks
946 switch (obs[sub].task)
947 {
948 case "STARTUP":
949 console.out(" STARTUP", "");
950 CloseLid();
951
952 doDrsCalibration("startup"); // will switch the voltage off
953
954 service_feedback.voltageOn();
955 service_feedback.waitForVoltageOn();
956
957 // Before we can switch to 3000 we have to make the right DRS calibration
958 console.out(" Take single p.e. run.");
959 while (!takeRun("pedestal", 5000));
960
961 // It is unclear what comes next, so we better switch off the voltage
962 service_feedback.voltageOff();
963 system_on = true;
964 break;
965
966 case "SHUTDOWN":
967 console.out(" SHUTDOWN", "");
968 Shutdown();
969 system_on = false;
970
971 // FIXME: Avoid new observations after a shutdown until
972 // the next startup (set run back to -2?)
973 console.out(" Waiting for next startup.", "");
974 sub++;
975 continue;
976
977 case "IDLE":
978 v8.sleep(1000);
979 continue;
980
981 case "DRSCALIB":
982 console.out(" DRSCALIB", "");
983 doDrsCalibration("drscalib"); // will switch the voltage off
984 break;
985
986 case "SINGLEPE":
987 console.out(" SINGLE-PE", "");
988
989 // The lid must be closes
990 CloseLid();
991
992 // Check if DRS calibration is necessary
993 var diff = getTimeSinceLastDrsCalib();
994 if (diff>30 || diff==null)
995 doDrsCalibration("singlepe"); // will turn voltage off
996
997 // The voltage must be on
998 service_feedback.voltageOn();
999 service_feedback.waitForVoltageOn();
1000
1001 // Before we can switch to 3000 we have to make the right DRS calibration
1002 console.out(" Take single p.e. run.");
1003 while (!takeRun("pedestal", 5000));
1004
1005 // It is unclear what comes next, so we better switch off the voltage
1006 service_feedback.voltageOff();
1007 break;
1008
1009 case "RATESCAN":
1010 console.out(" RATESCAN", "");
1011
1012 var tm1 = new Date();
1013
1014 // This is a workaround to make sure that we really catch
1015 // the new state and not the old one
1016 dim.send("DRIVE_CONTROL/STOP");
1017 dim.wait("DRIVE_CONTROL", "Armed", 5000);
1018
1019 // The lid must be open
1020 OpenLid();
1021
1022 // The voltage must be switched on
1023 service_feedback.voltageOn();
1024
1025 if (obs.source != undefined)
1026 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
1027 else
1028 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
1029
1030 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1031
1032 service_feedback.waitForVoltageOn();
1033
1034 var tm2 = new Date();
1035
1036 // Start rate scan
1037 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10);
1038
1039 // Lets wait if the ratescan really starts... this might take a few
1040 // seconds because RATE_SCAN configures the ftm and is waiting for
1041 // it to be configured.
1042 dim.wait("RATE_SCAN", "InProgress", 10000);
1043 dim.wait("RATE_SCAN", "Connected", 2700000);
1044
1045 // this line is actually some kind of hack.
1046 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
1047 // So I decided to put this line here as a kind of patchwork....
1048 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
1049
1050 console.out(" Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
1051 break; // case "RATESCAN"
1052
1053 case "DATA":
1054
1055 // ========================== case "DATA" ============================
1056 /*
1057 if (Sun.horizon("FACT").isUp)
1058 {
1059 console.out(" SHUTDOWN","");
1060 Shutdown();
1061 console.out(" Exit forced due to broken schedule", "");
1062 exit();
1063 }
1064 */
1065 // Calculate remaining time for this observation in minutes
1066 var remaining = nextObs==undefined ? 0 : (nextObs.start-new Date())/60000;
1067
1068 // ------------------------------------------------------------
1069
1070 console.out(" Run #"+run+" (remaining "+parseInt(remaining)+"min)");
1071
1072 // ----- Time since last DRS Calibration [min] ------
1073 var diff = getTimeSinceLastDrsCalib();
1074
1075 // Changine pointing position and take calibration...
1076 // ...every four runs (every ~20min)
1077 // ...if at least ten minutes of observation time are left
1078 // ...if this is the first run on the source
1079 var point = (run%4==0 && remaining>10) || run==0;
1080
1081 // Take DRS Calib...
1082 // ...every four runs (every ~20min)
1083 // ...at last every two hours
1084 // ...when DRS temperature has changed by more than 2deg (?)
1085 // ...when more than 15min of observation are left
1086 // ...no drs calibration was done yet
1087 var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null;
1088
1089 if (point)
1090 {
1091 // Change wobble position every four runs,
1092 // start with alternating wobble positions each day
1093 var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
1094
1095 //console.out(" Move telescope to '"+source+"' "+offset+" "+wobble);
1096 console.out(" Move telescope to '"+obs[sub].source+"' ["+wobble+"]");
1097
1098 //var offset = observations[obs][2];
1099 //var wobble = observations[obs][3 + parseInt(run/4)%2];
1100
1101 //dim.send("DRIVE_CONTROL/TRACK_SOURCE", offset, wobble, source);
1102
1103 dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
1104
1105 // Do we have to check if the telescope is really moving?
1106 // We can cross-check the SOURCE service later
1107 }
1108
1109 if (drscal)
1110 doDrsCalibration("data"); // will turn voltage off
1111
1112 OpenLid();
1113
1114 // voltage must be switched on after the lid is open for the
1115 // feedback to adapt the voltage properly to the night-sky
1116 // background light level.
1117 service_feedback.voltageOn();
1118
1119 // This is now th right time to wait for th drive to be stable
1120 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1121
1122 // Now we have to be prepared for data-taking:
1123 // make sure voltage is on
1124 service_feedback.waitForVoltageOn();
1125
1126 // If pointing had changed, do calibration
1127 if (point)
1128 {
1129 console.out(" Calibration.");
1130
1131 // Calibration (2% of 20')
1132 while (1)
1133 {
1134 if (!takeRun("pedestal", 1000)) // 80 Hz -> 10s
1135 continue;
1136 if (!takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s
1137 continue;
1138 break;
1139 }
1140 }
1141
1142 console.out(" Taking data: start [5min]");
1143
1144 var len = 300;
1145 while (len>0)
1146 {
1147 var time = new Date();
1148 if (takeRun("data", -1, len)) // Take data (5min)
1149 break;
1150
1151 len -= parseInt((new Date()-time)/1000);
1152 }
1153
1154 console.out(" Taking data: done");
1155 run++;
1156
1157 continue; // case "DATA"
1158 }
1159
1160 if (nextObs!=undefined && sub==obs.length-1)
1161 console.out(" Waiting for next observation scheduled for "+nextObs.start.toUTCString(),"");
1162
1163 sub++;
1164}
1165
1166sub_drsruns.close();
1167
1168// ================================================================
1169// Comments and ToDo goes here
1170// ================================================================
1171
1172// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
1173// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
1174//
1175// Arguments: TakeFirstDrsCalib
1176// To be determined: How to stop the script without foreceful interruption?
Note: See TracBrowser for help on using the repository browser.