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

Last change on this file since 15124 was 15117, checked in by tbretz, 12 years ago
Moved reading of the schedule to the beginning of the file so that typos get noticed before all the rest of the startup procedure.
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
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
31dimctrl.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
172function takeRun(type, count, time)
173{
174 if (!count)
175 count = -1;
176 if (!time)
177 time = -1;
178
179 console.out(" Take run: N="+count+" T="+time+"s ["+type+"]");
180
181 dim.send("MCP/START", time?time:-1, count?count:-1, type);
182
183 // What could be a reasonable timeout here?
184 // FIXME: Replace by callback?
185 //
186 // DN: I believe instead of waiting for 'TakingData' one could split this
187 // up into two checks with an extra condition:
188 // if type == 'data':
189 // wait until ThresholdCalibration starts:
190 // --> this time should be pretty identical for each run
191 // if this takes longer than say 3s:
192 // there might be a problem with one/more FADs
193 //
194 // wait until "TakingData":
195 // --> this seems to take even some minutes sometimes...
196 // (might be optimized rather soon, but still in the moment...)
197 // if this takes way too long:
198 // there might be something broken,
199 // so maybe a very high time limit is ok here.
200 // I think there is not much that can go wrong,
201 // when the Thr-Calib has already started. Still it might be nice
202 // If in the future RateControl is written so to find out that
203 // in case the threshold finding algorithm does
204 // *not converge as usual*
205 // it can complain, and in this way give a hint, that the weather
206 // might be a little bit too bad.
207 // else:
208 // wait until "TakingData":
209 // --> in a non-data run this time should be pretty short again
210 // if this takes longer than say 3s:
211 // there might be a problem with one/more FADs
212 //
213
214 if (!dim.wait("MCP", "TakingData", -300000) )
215 {
216 console.out("MCP took longer than 5 minutes to start TakingData");
217 console.out("maybe this idicates a problem with one of the FADs?");
218 dimctrl.setState(37);
219 dim.wait("MCP", "TakingData", 500);
220 }
221 dim.wait("MCP", "Idle");
222
223 console.out(" Take run: end");
224
225 // DN: currently reconnect() never returns false
226 // .. but it can fail of course.
227 if (!service_con.reconnect())
228 exit();
229
230 return true;//service_con.reconnect();
231}
232
233// ----------------------------------------------------------------
234
235function doDrsCalibration()
236{
237 console.out(" DRS cal: start");
238 service_feedback.voltageOff();
239
240 while (1)
241 {
242 dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
243 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
244 continue;
245 if (!takeRun("drs-gain", 1000)) // 40 / 20s (50Hz)
246 continue;
247 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
248 continue;
249
250 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
251 if (!takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
252 continue;
253 if (!takeRun("drs-time", 1000)) // 40 / 20s (50Hz)
254 continue;
255
256 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
257 if (!takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
258 continue;
259
260 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
261 if (!takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
262 continue;
263 // -----------
264 // 4'40 / 2'00
265
266 break;
267 }
268
269 dim.send("RATE_CONTROL/STOP"); // get out of GlobalThresholdSet
270 dim.wait("RATE_CONTROL", "Connected", 3000);
271
272 console.out(" DRS cal: end");
273}
274
275// ----------------------------------------------------------------
276
277function OpenLid()
278{
279 var horizon_parameter = "nautical";
280 while (Sun.horizon( horizon_parameter ).isUp)
281 {
282 var now = new Date();
283 var minutes_until_sunset = (Sun.horizon( horizon_parameter ).set - now)/60000;
284 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
285 v8.sleep(60000);
286 }
287
288 var isClosed = dim.state("LID_CONTROL").name=="Closed";
289
290 // Wait for lid to be open
291 if (isClosed)
292 {
293 console.out(" Open lid: start");
294 dim.send("LID_CONTROL/OPEN");
295 }
296 dim.wait("LID_CONTROL", "Open", 30000);
297
298 if (isClosed)
299 console.out(" Open lid: done");
300}
301
302function CloseLid()
303{
304 var isOpen = dim.state("LID_CONTROL").name=="Open";
305
306 // Wait for lid to be open
307 if (isOpen)
308 {
309 console.out(" Close lid: start");
310 dim.send("LID_CONTROL/CLOSE");
311 }
312 dim.wait("LID_CONTROL", "Closed", 30000);
313
314 if (isOpen)
315 console.out(" Close lid: end");
316}
317
318// ----------------------------------------------------------------
319
320var service_feedback = new Subscription("FEEDBACK/DEVIATION");
321
322// DN: Why is voltageOff() implemented as
323// a method of a Subscription to a specific Service
324// I naively would think of voltageOff() as an unbound function.
325// I seems to me it has to be a method of a Subscription object, in order
326// to use the update counting method. But does it have to be
327// a Subscription to FEEDBACK/DEVIATION, or could it work with other services as well?
328service_feedback.voltageOff = function()
329{
330 var state = dim.state("BIAS_CONTROL").name;
331
332 // check of feedback has to be switched on
333 var isOn = state=="VoltageOn" || state=="Ramping";
334 if (isOn)
335 {
336 console.out(" Voltage off: start");
337
338 // Supress the possibility that the bias control is
339 // ramping and will reject the command to switch the
340 // voltage off
341 var isControl = dim.state("FEEDBACK").name=="CurrentControl";
342 if (isControl)
343 {
344 console.out(" Suspending feedback.");
345 dim.send("FEEDBACK/ENABLE_OUTPUT", false);
346 dim.wait("FEEDBACK", "CurrentCtrlIdle", 3000);
347 }
348
349 // Switch voltage off
350 console.out(" Voltage on: switch off");
351 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
352
353 // If the feedback was enabled, re-enable it
354 if (isControl)
355 {
356 console.out(" Resuming feedback.");
357 dim.send("FEEDBACK/ENABLE_OUTPUT", true);
358 dim.wait("FEEDBACK", "CurrentControl", 3000);
359 }
360 }
361
362 dim.wait("BIAS_CONTROL", "VoltageOff", 5000);
363
364 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
365 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
366
367 if (isOn)
368 console.out(" Voltage off: end");
369}
370
371// DN: The name of the method voltageOn() in the context of the method
372// voltageOff() is a little bit misleading, since when voltageOff() returns
373// the caller can be sure the voltage is off, but when voltageOn() return
374// this is not the case, in the sense, that the caller can now take data.
375// instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
376// in order to safely take good-quality data.
377// This could lead to nasty bugs in the sense, that the second call might
378// be forgotten by somebody
379//
380// so I suggest to rename voltageOn() --> prepareVoltageOn()
381// waitForVoltageOn() stays as it is
382// and one creates a third method called:voltageOn() like this
383/* service_feedback.voltageOn = function()
384 * {
385 * this.prepareVoltageOn();
386 * this.waitForVoltageOn();
387 * }
388 *
389 * */
390// For convenience.
391
392service_feedback.voltageOn = function()
393{
394 //if (Sun.horizon("FACT").isUp)
395 // throw new Error("Sun is above FACT-horizon, voltage cannot be switched on.");
396
397 var isOff = dim.state("BIAS_CONTROL").name=="VoltageOff";
398 if (isOff)
399 {
400 console.out(" Voltage on: switch on");
401 //console.out(JSON.stringify(dim.state("BIAS_CONTROL")));
402
403 dim.send("BIAS_CONTROL/SET_GLOBAL_DAC", 1);
404 }
405
406 // Wait until voltage on
407 dim.wait("BIAS_CONTROL", "VoltageOn", 5000);
408
409 // From now on the feedback waits for a valid report from the FSC
410 // and than switchs to CurrentControl
411 dim.wait("FEEDBACK", "CurrentControl", 60000);
412
413 if (isOff)
414 {
415 this.cnt = this.get().counter;
416 console.out(" Voltage on: cnt="+this.cnt);
417 }
418}
419
420service_feedback.waitForVoltageOn = function()
421{
422 // waiting 45sec for the current control to stabilize...
423 // v8.sleep(45000);
424
425 // ----- Wait for at least three updates -----
426 // The feedback is started as if the camera where at 0deg
427 // Then after the first temp update, the temperature will be set to the
428 // correct value (this has already happened)
429 // So we only have to wait for the current to get stable.
430 // This should happen after three to five current updates.
431 // So we want one recent temperature update
432 // and three recent current updates
433
434 // Avoid output if condition is already fulfilled
435 if (this.cnt && this.get().counter>this.cnt+2)
436 return;
437
438 // FIXME: timeout missing
439 console.out(" Feedback wait: start");
440
441 var now = new Date();
442 while (this.cnt==undefined || this.get().counter<=this.cnt+2)
443 v8.sleep();
444
445 console.out(" Feedback wait: end [cnt=%d, %.2fs]".$(this.get().counter, (new Date()-now)/1000));
446}
447
448// ================================================================
449// Crosscheck all states
450// ================================================================
451
452include('scripts/Startup.js');//Startup();
453
454/*
455include('scripts/CheckStates.js');
456
457var table =
458[
459 [ "TNG_WEATHER" ],
460 [ "MAGIC_WEATHER" ],
461 [ "CHAT" ],
462 [ "SMART_FACT" ],
463 [ "FSC_CONTROL", [ "Connected" ] ],
464 [ "MCP", [ "Idle" ] ],
465 [ "TIME_CHECK", [ "Valid" ] ],
466 [ "PWR_CONTROL", [ "SystemOn" ] ],
467 [ "AGILENT_CONTROL", [ "VoltageOn" ] ],
468 [ "BIAS_CONTROL", [ "VoltageOff" ] ],
469 [ "FEEDBACK", [ "CurrentControl", "CurrentCtrlIdle", "Connected" ] ],
470 [ "RATE_SCAN", [ "Connected" ] ],
471 [ "RATE_CONTROL", [ "Connected" ] ],
472 [ "LID_CONTROL", [ "Open", "Closed" ] ],
473 [ "DRIVE_CONTROL", [ "Armed", "Tracking", "OnTrack" ] ],
474 [ "FTM_CONTROL", [ "Idle", "TriggerOn" ] ],
475 [ "FAD_CONTROL", [ "Connected", "WritingData" ] ],
476 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
477];
478
479console.out("Checking states.");
480if (!checkStates(table, 10000))
481{
482 throw new Error"Something unexpected has happened. Although the startup-",
483 "procedure has finished, not all servers are in the state",
484 "in which they ought to be. Please, try to find out what",
485 "happened...");
486}
487
488console.out("Checking states: done.");
489*/
490// ----------------------------------------------------------------
491
492console.out("Checking send.");
493checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
494console.out("Checking send: done");
495
496// ----------------------------------------------------------------
497
498console.out("Feedback init: start.");
499service_feedback.get(5000);
500
501dim.send("FEEDBACK/ENABLE_OUTPUT", true);
502dim.send("FEEDBACK/START_CURRENT_CONTROL", 0.);
503
504console.out("Feedback init: end.");
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.