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

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