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

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