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

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