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

Last change on this file since 17179 was 17179, checked in by tbretz, 11 years ago
Reverting to last revision.
File size: 33.3 KB
Line 
1/**
2 * @fileOverview This file has functions related to documenting JavaScript.
3 * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
4 */
5'use strict';
6
7dim.log("Start: "+__FILE__+" ["+__DATE__+"]");
8
9// This should be set in dimctrl.rc as JavaScript.schedule-database.
10// It is sent together with the script to the dimserver.
11// If started directly, it has to be set after the command:
12//
13// .js scripts/Main.js schedule-database=...
14//
15if (!$['schedule-database'])
16 throw new Error("Environment 'schedule-database' not set!");
17
18//dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
19
20// ================================================================
21// Code related to the schedule
22// ================================================================
23
24//this is just the class implementation of 'Observation'
25include('scripts/Observation_class.js');
26include('scripts/getSchedule.js');
27
28var observations = [ ];
29
30// Get the observation scheduled for 'now' from the table and
31// return its index
32function getObservation(now)
33{
34 if (now==undefined)
35 now = new Date();
36
37 if (isNaN(now.valueOf()))
38 throw new Error("Date argument in getObservation invalid.");
39
40 observations = getSchedule();
41
42 for (var i=0; i<observations.length; i++)
43 if (now<observations[i].start)
44 return i-1;
45
46 return observations.length-1;
47}
48
49// ================================================================
50// Code to check whether observation is allowed
51// ================================================================
52/*
53function currentEst(source)
54{
55 var moon = new Moon();
56 if (!moon.isUp)
57 return 7.7;
58
59 var dist = Sky.dist(moon, source);
60
61 var alt = 90-moon.toLocal().zd;
62
63 var lc = dist*alt*pow(Moon.disk(), 6)/360/360;
64
65 var cur = 7.7+4942*lc;
66
67 return cur;
68}
69
70function thresholdEst(source) // relative threshold (ratio)
71{
72 // Assumption:
73 // atmosphere is 70km, shower taks place after 60km, earth radius 6400km
74 // just using the cosine law
75 // This fits very well with MC results: See Roger Firpo, p.45
76 // "Study of the MAGIC telescope sensitivity for Large Zenith Angle observations"
77
78 var c = Math.cos(Math.Pi-source.zd);
79 var ratio = (10*sqrt(409600*c*c+9009) + 6400*c - 60)/10;
80
81 // assumption: Energy threshold increases linearily with current
82 // assumption: Energy threshold increases linearily with distance
83
84 return ratio*currentEst(source)/7.7;
85}
86*/
87
88// ================================================================
89// Code to perform the DRS calib sequence
90// ================================================================
91
92var irq;
93
94function doDrsCalibration(where)
95{
96 dim.log("Starting DRS calibration ["+where+"]");
97
98 service_feedback.voltageOff();
99
100 var tm = new Date();
101
102 while (!irq)
103 {
104 dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
105 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
106 continue;
107
108 if (irq || !takeRun("drs-gain", 1000)) // 40 / 20s (50Hz)
109 continue;
110
111 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
112 continue;
113
114 break;
115 }
116
117 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
118
119 while (!irq && !takeRun("drs-pedestal", 1000)); // 40 / 20s (50Hz)
120 while (!irq && !takeRun("drs-time", 1000)); // 40 / 20s (50Hz)
121
122 while (!irq)
123 {
124 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
125 if (takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
126 break;
127 }
128
129 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
130
131 while (!irq && !takeRun("pedestal", 1000)); // 40 / 10s (80Hz)
132 // -----------
133 // 4'40 / 2'00
134
135 if (irq)
136 dim.log("DRS calibration interrupted [%.1fs]".$((new Date()-tm)/1000));
137 else
138 dim.log("DRS calibration done [%.1fs]".$((new Date()-tm)/1000));
139}
140
141// ================================================================
142// Code related to the lid
143// ================================================================
144
145function OpenLid()
146{
147 /*
148 while (Sun.horizon(-13).isUp)
149 {
150 var now = new Date();
151 var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
152 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
153 v8.sleep(60000);
154 }*/
155
156 var isClosed = dim.state("LID_CONTROL").name=="Closed";
157
158 var tm = new Date();
159
160 // Wait for lid to be open
161 if (isClosed)
162 {
163 dim.log("Opening lid");
164 dim.send("LID_CONTROL/OPEN");
165 }
166 dim.wait("LID_CONTROL", "Open", 30000);
167
168 if (isClosed)
169 dim.log("Lid open [%.1fs]".$((new Date()-tm)/1000));
170}
171
172function CloseLid()
173{
174 var isOpen = dim.state("LID_CONTROL").name=="Open";
175
176 var tm = new Date();
177
178 // Wait for lid to be open
179 if (isOpen)
180 {
181 dim.log("Closing lid.");
182 dim.send("LID_CONTROL/CLOSE");
183 }
184 dim.wait("LID_CONTROL", "Closed", 30000);
185
186 if (isOpen)
187 dim.log("Lid closed [%.1fs]".$((new Date()-tm)/1000));
188}
189
190// ================================================================
191// Code related to switching bias voltage on and off
192// ================================================================
193
194var service_feedback = new Subscription("FEEDBACK/CALIBRATED_CURRENTS");
195
196service_feedback.onchange = function(evt)
197{
198 if (!evt.data)
199 return;
200
201 var Unom = evt.obj['U_nom'];
202 var Uov = evt.obj['U_ov'];
203 if (!Uov)
204 return;
205
206 var cnt = 0;
207 var avg = 0;
208 for (var i=0; i<320; i++)
209 {
210 var dU = Uov[i]-Unom;
211
212 // 0.022 corresponds to 1 DAC count (90V/4096)
213 if (Math.abs(dU)>0.033)
214 cnt++;
215
216 avg += dU;
217 }
218 avg /= 320;
219
220 if (this.ok==undefined)
221 return;
222
223 this.ok = cnt==0;
224
225 console.out(" DeltaUov=%.3f [N(>0.033V)=%d]".$(avg, cnt));
226}
227
228// DN: Why is voltageOff() implemented as
229// a method of a Subscription to a specific Service
230// I naively would think of voltageOff() as an unbound function.
231// I seems to me it has to be a method of a Subscription object, in order
232// to use the update counting method. But does it have to be
233// a Subscription to FEEDBACK/DEVIATION, or could it work with other services as well?
234service_feedback.voltageOff = function()
235{
236 var state = dim.state("BIAS_CONTROL").name;
237
238 if (state=="Disconnected")
239 {
240 console.out(" Voltage off: bias crate disconnected!");
241 return;
242 }
243
244 // check of feedback has to be switched on
245 var isOn = state=="VoltageOn" || state=="Ramping";
246 if (isOn)
247 {
248 dim.log("Switching voltage off.");
249
250 this.ok = undefined;
251
252 // Supress the possibility that the bias control is
253 // ramping and will reject the command to switch the
254 // voltage off
255 dim.send("FEEDBACK/STOP");
256 dim.wait("FEEDBACK", "Calibrated", 3000);
257
258 // Switch voltage off
259 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
260 }
261
262 dim.wait("BIAS_CONTROL", "VoltageOff", 60000);
263
264 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
265 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
266
267 if (isOn)
268 dim.log("Voltage off.");
269}
270
271// DN: The name of the method voltageOn() in the context of the method
272// voltageOff() is a little bit misleading, since when voltageOff() returns
273// the caller can be sure the voltage is off, but when voltageOn() return
274// this is not the case, in the sense, that the caller can now take data.
275// instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
276// in order to safely take good-quality data.
277// This could lead to nasty bugs in the sense, that the second call might
278// be forgotten by somebody
279//
280// so I suggest to rename voltageOn() --> prepareVoltageOn()
281// waitForVoltageOn() stays as it is
282// and one creates a third method called:voltageOn() like this
283/* service_feedback.voltageOn = function()
284 * {
285 * this.prepareVoltageOn();
286 * this.waitForVoltageOn();
287 * }
288 *
289 * */
290// For convenience.
291
292service_feedback.voltageOn = function()
293{
294 //if (Sun.horizon("FACT").isUp)
295 // throw new Error("Sun is above FACT-horizon, voltage cannot be switched on.");
296
297 var isOff = dim.state("BIAS_CONTROL").name=="VoltageOff";
298 if (isOff)
299 {
300 dim.log("Switching voltage on.");
301
302 dim.send("FEEDBACK/START", 1.1);
303 dim.wait("FEEDBACK", "InProgress", 45000);
304 }
305
306 // Wait until voltage on
307 dim.wait("BIAS_CONTROL", "VoltageOn", 60000);
308
309 if (isOff)
310 this.ok = false;
311}
312
313service_feedback.waitForVoltageOn = function()
314{
315 // Avoid output if condition is already fulfilled
316 dim.log("Waiting for voltage to be stable.");
317
318 function func()
319 {
320 if (irq || this.ok==true)
321 return true;
322 }
323
324 var now = new Date();
325
326 v8.timeout(60000, func, this);
327
328 if (irq)
329 dim.log("Waiting for stable voltage interrupted.");
330 else
331 dim.log("Voltage stable within limits");
332
333 this.ok = undefined;
334}
335
336// ================================================================
337// Function to shutdown the system
338// ================================================================
339
340function Shutdown()
341{
342 dim.log("Starting shutdown.");
343
344 var now1 = new Date();
345
346 service_feedback.voltageOff();
347 CloseLid();
348
349 var now2 = new Date();
350
351 dim.send("DRIVE_CONTROL/PARK");
352
353 console.out("","Waiting for telescope to park. This may take a while.");
354
355 // FIXME: This might not work is the drive is already close to park position
356 dim.wait("DRIVE_CONTROL", "Locked", 3000);
357
358 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
359 sub.get(5000); // FIXME: Proper error message in case of failure
360
361 function func()
362 {
363 var report = sub.get();
364
365 var zd = report.obj['Zd'];
366 var az = report.obj['Az'];
367
368 if (zd>100 && Math.abs(az)<1)
369 return true;
370
371 return undefined;
372 }
373
374 v8.timeout(150000, func);
375
376 var now3 = new Date();
377
378 //dim.send("FEEDBACK/STOP");
379 dim.send("FTM_CONTROL/STOP_TRIGGER");
380 dim.send("BIAS_CONTROL/DISCONNECT");
381
382 dim.wait("FTM_CONTROL", "Valid", 3000);
383 dim.wait("BIAS_CONTROL", "Disconnected", 3000);
384 dim.wait("FEEDBACK", "Calibrated", 3000);
385
386 var report = sub.get();
387
388 console.out("");
389 console.out("Shutdown procedure seems to be finished...");
390 console.out(" "+new Date().toUTCString());
391 console.out(" Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
392 console.out(" Please make sure the park position was reached");
393 console.out(" and the telescope is not moving anymore.");
394 console.out(" Please check that the lid is closed and the voltage switched off.", "");
395 console.out(" DRIVE_CONTROL: "+dim.state("DRIVE_CONTROL").name);
396 console.out(" FEEDBACK: "+dim.state("FEEDBACK").name);
397 console.out(" FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
398 console.out(" BIAS_CONTROL: "+dim.state("BIAS_CONTROL").name);
399 console.out("");
400 dim.log("Shutdown: end ["+(now2-now1)/1000+"s, "+(now3-now2)/1000+"s, "+(new Date()-now3)/1000+"s]");
401 console.out("");
402
403 sub.close();
404}
405
406// ================================================================
407// Check datalogger subscriptions
408// ================================================================
409
410var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS");
411datalogger_subscriptions.get(3000, false);
412
413datalogger_subscriptions.check = function()
414{
415 var obj = this.get();
416 if (!obj.data)
417 throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available.");
418
419 var expected =
420 [
421 "BIAS_CONTROL/CURRENT",
422 "BIAS_CONTROL/DAC",
423 "BIAS_CONTROL/NOMINAL",
424 "BIAS_CONTROL/VOLTAGE",
425 "DRIVE_CONTROL/POINTING_POSITION",
426 "DRIVE_CONTROL/SOURCE_POSITION",
427 "DRIVE_CONTROL/STATUS",
428 "DRIVE_CONTROL/TRACKING_POSITION",
429 "FAD_CONTROL/CONNECTIONS",
430 "FAD_CONTROL/DAC",
431 "FAD_CONTROL/DNA",
432 "FAD_CONTROL/DRS_RUNS",
433 "FAD_CONTROL/EVENTS",
434 "FAD_CONTROL/FEEDBACK_DATA",
435 "FAD_CONTROL/FILE_FORMAT",
436 "FAD_CONTROL/FIRMWARE_VERSION",
437 "FAD_CONTROL/INCOMPLETE",
438 "FAD_CONTROL/PRESCALER",
439 "FAD_CONTROL/REFERENCE_CLOCK",
440 "FAD_CONTROL/REGION_OF_INTEREST",
441 "FAD_CONTROL/RUNS",
442 "FAD_CONTROL/RUN_NUMBER",
443 "FAD_CONTROL/START_RUN",
444 "FAD_CONTROL/STATISTICS1",
445 "FAD_CONTROL/STATS",
446 "FAD_CONTROL/STATUS",
447 "FAD_CONTROL/TEMPERATURE",
448 "FEEDBACK/CALIBRATED_CURRENTS",
449 "FEEDBACK/CALIBRATION",
450 "FEEDBACK/CALIBRATION_R8",
451 "FEEDBACK/CALIBRATION_STEPS",
452/* "FEEDBACK/REFERENCE",*/
453 "FSC_CONTROL/CURRENT",
454 "FSC_CONTROL/HUMIDITY",
455 "FSC_CONTROL/TEMPERATURE",
456 "FSC_CONTROL/VOLTAGE",
457 "FTM_CONTROL/COUNTER",
458 "FTM_CONTROL/DYNAMIC_DATA",
459 "FTM_CONTROL/ERROR",
460 "FTM_CONTROL/FTU_LIST",
461 "FTM_CONTROL/PASSPORT",
462 "FTM_CONTROL/STATIC_DATA",
463 "FTM_CONTROL/TRIGGER_RATES",
464 "LID_CONTROL/DATA",
465 "MAGIC_LIDAR/DATA",
466 "MAGIC_WEATHER/DATA",
467 "MCP/CONFIGURATION",
468 "PWR_CONTROL/DATA",
469 "RATE_CONTROL/THRESHOLD",
470 "RATE_SCAN/DATA",
471 "RATE_SCAN/PROCESS_DATA",
472 "TEMPERATURE/DATA",
473 "TIME_CHECK/OFFSET",
474 "TNG_WEATHER/DATA",
475 "TNG_WEATHER/DUST",
476 ];
477
478 function map(entry)
479 {
480 if (entry.length==0)
481 return undefined;
482
483 var rc = entry.split(',');
484 if (rc.length!=2)
485 throw new Error("Subscription list entry '"+entry+"' has wrong number of elements.");
486 return rc;
487 }
488
489 var list = obj.data.split('\n').map(map);
490
491 function check(name)
492 {
493 if (list.every(function(el){return el==undefined || el[0]!=name;}))
494 throw new Error("Subscription to '"+name+"' not available.");
495 }
496
497 expected.forEach(check);
498}
499
500
501
502// ================================================================
503// Crosscheck all states
504// ================================================================
505
506// ----------------------------------------------------------------
507// Do a standard startup to bring the system in into a well
508// defined state
509// ----------------------------------------------------------------
510include('scripts/Startup.js');
511
512// ================================================================
513// Code to monitor clock conditioner
514// ================================================================
515
516var sub_counter = new Subscription("FTM_CONTROL/COUNTER");
517sub_counter.onchange = function(evt)
518{
519 if (evt.qos>0 && evt.qos!=2 && evt.qos&0x100==0)
520 throw new Error("FTM reports: clock conditioner not locked.");
521}
522
523// ================================================================
524// Code related to monitoring the fad system
525// ================================================================
526
527// This code is here, because scripts/Startup.js needs the
528// same subscriptions... to be revised.
529var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
530var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
531var sub_startrun = new Subscription("FAD_CONTROL/START_RUN");
532sub_startrun.get(5000);
533
534include('scripts/takeRun.js');
535
536// ----------------------------------------------------------------
537// Check that everything we need is availabel to receive commands
538// (FIXME: Should that go to the general CheckState?)
539// ----------------------------------------------------------------
540//console.out("Checking send.");
541checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
542//console.out("Checking send: done");
543
544// ----------------------------------------------------------------
545// Bring feedback into the correct operational state
546// ----------------------------------------------------------------
547//console.out("Feedback init: start.");
548service_feedback.get(5000);
549
550//v8.timeout(3000, function() { var n = dim.state("FEEDBACK").name; if (n=="CurrentCtrlIdle" || n=="CurrentControl") return true; });
551
552// ----------------------------------------------------------------
553// Connect to the DRS_RUNS service
554// ----------------------------------------------------------------
555//console.out("Drs runs init: start.");
556
557var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
558sub_drsruns.get(5000);
559// FIXME: Check if the last DRS calibration was complete?
560
561function getTimeSinceLastDrsCalib()
562{
563 // ----- Time since last DRS Calibration [min] ------
564 var runs = sub_drsruns.get(0);
565 var diff = (new Date()-runs.time)/60000;
566
567 // Warning: 'roi=300' is a number which is not intrisically fixed
568 // but can change depending on the taste of the observers
569 var valid = runs.obj['run'][2]>0 && runs.obj['roi']==300;
570
571 if (valid)
572 dim.log("Last DRS calibration was %.1fmin ago".$(diff));
573 else
574 dim.log("No valid DRS calibration available.");
575
576 return valid ? diff : null;
577}
578
579// ----------------------------------------------------------------
580// Install interrupt handler
581// ----------------------------------------------------------------
582function handleIrq(cmd, args, time, user)
583{
584 console.out("Interrupt received:");
585 console.out(" IRQ: "+cmd);
586 console.out(" Time: "+time);
587 console.out(" User: "+user);
588
589 irq = cmd ? cmd : "stop";
590
591 // This will end a run in progress as if it where correctly stopped
592 if (dim.state("MCP").name=="TakingData")
593 dim.send("MCP/STOP");
594
595 // This will stop a rate scan in progress
596 if (dim.state("RATE_SCAN").name=="InProgress")
597 dim.send("RATE_SCAN/STOP");
598}
599
600dimctrl.setInterruptHandler(handleIrq);
601
602// ----------------------------------------------------------------
603// Make sure we will write files
604// ----------------------------------------------------------------
605dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
606
607// ----------------------------------------------------------------
608// Print some information for the user about the
609// expected first oberservation
610// ----------------------------------------------------------------
611var test = getObservation();
612if (test!=undefined)
613{
614 var n = new Date();
615 if (observations.length>0 && test==-1)
616 dim.log("First observation scheduled for "+observations[0].start.toUTCString()+" [id="+observations[0].id+"]");
617 if (test>=0 && test<observations.length)
618 dim.log("First observation should start immediately ["+observations[test].start.toUTCString()+", id="+observations[test].id+"]");
619 if (observations.length>0 && observations[0].start>n+12*3600*1000)
620 dim.log("No observations scheduled for the next 12 hours!");
621 if (observations.length==0)
622 dim.log("No observations scheduled!");
623}
624
625// ----------------------------------------------------------------
626// Start main loop
627// ----------------------------------------------------------------
628dim.log("Entering main loop.");
629
630var run = -2; // getObservation never called
631var sub;
632var lastId;
633var nextId;
634var sun = Sun.horizon(-12);
635var system_on; // undefined
636
637while (!irq)
638{
639 // Check if observation position is still valid
640 // If source position has changed, set run=0
641 var idxObs = getObservation();
642 if (idxObs===undefined)
643 break;
644
645 // we are still waiting for the first observation in the schedule
646 if (idxObs==-1)
647 {
648 // flag that the first observation will be in the future
649 run = -1;
650 v8.sleep(1000);
651 continue;
652 }
653
654 // Check if we have to take action do to sun-rise
655 var was_up = sun.isUp;
656 sun = Sun.horizon(-12);
657 if (!was_up && sun.isUp)
658 {
659 console.out("");
660 dim.log("Sun rise detected.... automatic shutdown initiated!");
661 // FIXME: State check?
662 Shutdown();
663 system_on = false;
664 continue;
665 }
666
667 // Current and next observation target
668 var obs = observations[idxObs];
669 var nextObs = observations[idxObs+1];
670
671 // Check if observation target has changed
672 if (lastId!=obs.id) // !Object.isEqual(obs, nextObs)
673 {
674 console.out("");
675 dim.log("Starting new observation ["+obs.start.toUTCString()+", id="+obs.id+"]");
676
677 // This is the first source, but we do not come from
678 // a scheduled 'START', so we have to check if the
679 // telescop is operational already
680 sub = 0;
681 if (run<0)
682 {
683 //Startup(); // -> Bias On/Off?, Lid open/closed?
684 //CloseLid();
685 }
686
687 // The first observation had a start-time in the past...
688 // In this particular case start with the last entry
689 // in the list of measurements
690 if (run==-2)
691 sub = obs.length-1;
692
693 run = 0;
694 lastId = obs.id;
695 }
696
697 if (nextObs && nextId!=nextObs.id)
698 {
699 dim.log("Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
700 console.out("");
701 nextId = nextObs.id;
702 }
703
704 if (!nextObs && nextId)
705 {
706 dim.log("No further observation scheduled.");
707 console.out("");
708 nextId = undefined;
709 }
710
711 //if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
712 // throw Error("Last scheduled measurement must be a shutdown.");
713
714 // We are done with all measurement slots for this
715 // observation... wait for next observation
716 if (sub>=obs.length)
717 {
718 v8.sleep(1000);
719 continue;
720 }
721
722 if (system_on===false && obs[sub].task!="STARTUP")
723 {
724 v8.sleep(1000);
725 continue;
726 }
727
728 // Check if sun is still up... only DATA and RATESCAN must be suppressed
729 if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN") && sun.isUp)
730 {
731 var now = new Date();
732 var remaining = (sun.set - now)/60000;
733 console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
734 v8.sleep(60000);
735 continue;
736 }
737
738
739 if (obs[sub].task!="IDLE" && (obs[sub].task!="DATA" && run>0))
740 dim.log("New task ["+obs[sub]+"]");
741
742 // FIXME: Maybe print a warning if Drive is on during day time!
743
744 // It is not ideal that we allow the drive to be on during day time, but
745 // otherwise it is difficult to allow e.g. the STARTUP at the beginning of the night
746 var power_states = sun.isUp || !system_on ? [ "DriveOff", "SystemOn" ] : [ "SystemOn" ];
747 var drive_states = sun.isUp || !system_on ? undefined : [ "Armed", "Tracking", "OnTrack" ];
748
749 // A scheduled task was found, lets check if all servers are
750 // still only and in reasonable states. If this is not the case,
751 // something unexpected must have happend and the script is aborted.
752 //console.out(" Checking states [general]");
753 var table =
754 [
755 [ "TNG_WEATHER" ],
756 [ "MAGIC_WEATHER" ],
757 [ "CHAT" ],
758 [ "SMART_FACT" ],
759 [ "TEMPERATURE" ],
760 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
761 [ "FSC_CONTROL", [ "Connected" ] ],
762 [ "MCP", [ "Idle" ] ],
763 [ "TIME_CHECK", [ "Valid" ] ],
764 [ "PWR_CONTROL", power_states/*[ "SystemOn" ]*/ ],
765 [ "AGILENT_CONTROL", [ "VoltageOn" ] ],
766 [ "BIAS_CONTROL", [ "VoltageOff", "VoltageOn", "Ramping" ] ],
767 [ "FEEDBACK", [ "Calibrated", "InProgress" ] ],
768 [ "LID_CONTROL", [ "Open", "Closed" ] ],
769 [ "DRIVE_CONTROL", drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
770 [ "FTM_CONTROL", [ "Valid", "TriggerOn" ] ],
771 [ "FAD_CONTROL", [ "Connected", "RunInProgress" ] ],
772 [ "RATE_SCAN", [ "Connected" ] ],
773 [ "RATE_CONTROL", [ "Connected", "GlobalThresholdSet", "InProgress" ] ],
774 ];
775
776
777 if (!checkStates(table))
778 {
779 throw new Error("Something unexpected has happened. One of the servers "+
780 "is in a state in which it should not be. Please,"+
781 "try to find out what happened...");
782 }
783
784 datalogger_subscriptions.check();
785
786 // Check if obs.task is one of the one-time-tasks
787 switch (obs[sub].task)
788 {
789 case "IDLE":
790 v8.sleep(5000);
791 continue;
792
793 case "STARTUP":
794 CloseLid();
795
796 doDrsCalibration("startup"); // will switch the voltage off
797
798 if (irq)
799 break;
800
801 service_feedback.voltageOn();
802 service_feedback.waitForVoltageOn();
803
804 // Before we can switch to 3000 we have to make the right DRS calibration
805 dim.log("Taking single p.e. run.");
806 while (!irq && !takeRun("single-pe", 10000));
807
808 // It is unclear what comes next, so we better switch off the voltage
809 service_feedback.voltageOff();
810 system_on = true;
811 dim.log("Task finished [STARTUP]");
812 console.out("");
813 break;
814
815 case "SHUTDOWN":
816 Shutdown();
817 system_on = false;
818
819 // FIXME: Avoid new observations after a shutdown until
820 // the next startup (set run back to -2?)
821 sub++;
822 dim.log("Task finished [SHUTDOWN]");
823 console.out("");
824 //console.out(" Waiting for next startup.", "");
825 continue;
826
827 case "DRSCALIB":
828 doDrsCalibration("drscalib"); // will switch the voltage off
829 dim.log("Task finished [DRSCALIB]");
830 console.out("");
831 break;
832
833 case "SINGLEPE":
834 // The lid must be closes
835 CloseLid();
836
837 // Check if DRS calibration is necessary
838 var diff = getTimeSinceLastDrsCalib();
839 if (diff>30 || diff==null)
840 {
841 doDrsCalibration("singlepe"); // will turn voltage off
842 if (irq)
843 break;
844 }
845
846 // The voltage must be on
847 service_feedback.voltageOn();
848 service_feedback.waitForVoltageOn();
849
850 // Before we can switch to 3000 we have to make the right DRS calibration
851 dim.log("Taking single p.e. run.");
852 while (!irq && !takeRun("single-pe", 10000));
853
854 // It is unclear what comes next, so we better switch off the voltage
855 service_feedback.voltageOff();
856 dim.log("Task finished [SINGLE-PE]");
857 console.out("");
858 break;
859
860 case "RATESCAN":
861 var tm1 = new Date();
862
863 // This is a workaround to make sure that we really catch
864 // the new OnTrack state later and not the old one
865 dim.send("DRIVE_CONTROL/STOP");
866 dim.wait("DRIVE_CONTROL", "Armed", 15000);
867
868 // The lid must be open
869 OpenLid();
870
871 // The voltage must be switched on
872 service_feedback.voltageOn();
873
874 if (obs.source != undefined)
875 {
876 dim.log("Pointing telescope to '"+obs[cub].source+"'.");
877 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
878 }
879 else
880 {
881 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
882 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
883 }
884
885 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
886
887 service_feedback.waitForVoltageOn();
888
889 var tm2 = new Date();
890
891 dim.log("Starting ratescan.");
892
893 // Start rate scan
894 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10);
895
896 // Lets wait if the ratescan really starts... this might take a few
897 // seconds because RATE_SCAN configures the ftm and is waiting for
898 // it to be configured.
899 dim.wait("RATE_SCAN", "InProgress", 10000);
900 dim.wait("RATE_SCAN", "Connected", 2700000);
901
902 // this line is actually some kind of hack.
903 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
904 // So I decided to put this line here as a kind of patchwork....
905 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
906
907 dim.log("Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
908 dim.log("Task finished [RATESCAN]");
909 console.out("");
910 break; // case "RATESCAN"
911
912
913 case "DATA":
914
915 // ========================== case "DATA" ============================
916 /*
917 if (Sun.horizon("FACT").isUp)
918 {
919 console.out(" SHUTDOWN","");
920 Shutdown();
921 console.out(" Exit forced due to broken schedule", "");
922 exit();
923 }
924 */
925 // Calculate remaining time for this observation in minutes
926 var remaining = nextObs==undefined ? 0 : (nextObs.start-new Date())/60000;
927
928 // ------------------------------------------------------------
929
930 dim.log("Run count "+run+" [remaining "+parseInt(remaining)+"min]");
931
932 // ----- Time since last DRS Calibration [min] ------
933 var diff = getTimeSinceLastDrsCalib();
934
935 // Changine pointing position and take calibration...
936 // ...every four runs (every ~20min)
937 // ...if at least ten minutes of observation time are left
938 // ...if this is the first run on the source
939 var point = (run%4==0 && remaining>10) || run==0;
940
941 // Take DRS Calib...
942 // ...every four runs (every ~20min)
943 // ...at last every two hours
944 // ...when DRS temperature has changed by more than 2deg (?)
945 // ...when more than 15min of observation are left
946 // ...no drs calibration was done yet
947 var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null;
948
949 if (point)
950 {
951 // Change wobble position every four runs,
952 // start with alternating wobble positions each day
953 var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
954
955 //console.out(" Move telescope to '"+source+"' "+offset+" "+wobble);
956 dim.log("Pointing telescope to '"+obs[sub].source+"' [wobble="+wobble+"]");
957
958 // This is a workaround to make sure that we really catch
959 // the new OnTrack state later and not the old one
960 dim.send("DRIVE_CONTROL/STOP");
961 dim.wait("DRIVE_CONTROL", "Armed", 15000);
962
963 dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
964
965 // Do we have to check if the telescope is really moving?
966 // We can cross-check the SOURCE service later
967 }
968
969 if (drscal)
970 {
971 doDrsCalibration("data"); // will turn voltage off
972
973 // Now we switch on the voltage and a significant amount of
974 // time has been passed, so do the check again.
975 sun = Sun.horizon(-12);
976 if (!was_up && sun.isUp)
977 {
978 dim.log("Sun rise detected....");
979 continue;
980 }
981 }
982
983 if (irq)
984 break;
985
986 OpenLid();
987
988
989 // voltage must be switched on after the lid is open for the
990 // feedback to adapt the voltage properly to the night-sky
991 // background light level.
992 service_feedback.voltageOn();
993
994 // This is now th right time to wait for th drive to be stable
995 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
996
997 // Now we have to be prepared for data-taking:
998 // make sure voltage is on
999 service_feedback.waitForVoltageOn();
1000
1001 // If pointing had changed, do calibration
1002 if (point)
1003 {
1004 dim.log("Starting calibration.");
1005
1006 // Calibration (2% of 20')
1007 while (!irq)
1008 {
1009 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s
1010 continue;
1011 if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s
1012 continue;
1013 break;
1014 }
1015 }
1016
1017 //console.out(" Taking data: start [5min]");
1018
1019 // FIXME: What do we do if during calibration something has happened
1020 // e.g. drive went to ERROR? Maybe we have to check all states again?
1021
1022 var twilight = Sun.horizon(-16).isUp;
1023
1024 if (twilight)
1025 {
1026 for (var i=0; i<5 && !irq; i++)
1027 takeRun("data", -1, 60); // Take data (1min)
1028 }
1029 else
1030 {
1031 var len = 300;
1032 while (len>15)
1033 {
1034 var time = new Date();
1035 if (takeRun("data", -1, len)) // Take data (5min)
1036 break;
1037
1038 len -= parseInt((new Date()-time)/1000);
1039 }
1040 }
1041
1042 //console.out(" Taking data: done");
1043 run++;
1044
1045 continue; // case "DATA"
1046 }
1047
1048 if (nextObs!=undefined && sub==obs.length-1)
1049 {
1050 dim.log("Next observation will start at "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
1051 console.out("");
1052 }
1053
1054 sub++;
1055}
1056
1057sub_drsruns.close();
1058
1059dim.log("Left main loop [irq="+irq+"]");
1060
1061if (irq.toUpperCase()=="SHUTDOWN")
1062 Shutdown();
1063
1064// ================================================================
1065// Comments and ToDo goes here
1066// ================================================================
1067
1068// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
1069// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
Note: See TracBrowser for help on using the repository browser.