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

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