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

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