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

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