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

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