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

Last change on this file since 18679 was 18651, checked in by tbretz, 8 years ago
That was the wrong order. First use sub then increment.
File size: 51.5 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 (where!="data")
112 {
113 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
114 continue;
115 }
116
117 break;
118 }
119
120 if (where!="data")
121 {
122 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
123
124 while (!irq && !takeRun("drs-pedestal", 1000)); // 40 / 20s (50Hz)
125 while (!irq && !takeRun("drs-time", 1000)); // 40 / 20s (50Hz)
126 }
127
128 while (!irq)
129 {
130 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
131 if (takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
132 break;
133 }
134
135 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
136
137 while (!irq && !takeRun("pedestal", 1000)); // 40 / 10s (80Hz)
138 // -----------
139 // 4'40 / 2'00
140
141 if (irq)
142 dim.log("DRS calibration interrupted [%.1fs]".$((new Date()-tm)/1000));
143 else
144 dim.log("DRS calibration done [%.1fs]".$((new Date()-tm)/1000));
145}
146
147// ================================================================
148// Code related to the lid
149// ================================================================
150
151function OpenLid()
152{
153 /*
154 while (Sun.horizon(-13).isUp)
155 {
156 var now = new Date();
157 var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
158 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
159 v8.sleep(60000);
160 }*/
161
162 var isClosed = dim.state("LID_CONTROL").name=="Closed";
163 var isInconsistent = dim.state("LID_CONTROL").name=="Inconsistent";
164
165 var tm = new Date();
166
167 // Wait for lid to be open
168 if (isClosed || isInconsistent)
169 {
170 dim.log("Opening lid");
171 dim.send("LID_CONTROL/OPEN");
172
173 dim.log("Turning off IR camera LEDs...");
174
175 var cam = new Curl("fact@cam/cgi-bin/user/Config.cgi");
176 cam.data.push("action=set");
177 cam.data.push("Camera.System.Title=Camera1");
178 cam.data.push("Camera.General.IRControl.Value=2");
179 cam.data.push("Camera.System.Display=ALL");
180 cam.data.push("Camera.Environment=OUTDOOR");
181 var ret = cam.send();
182 dim.log("Camera response: "+ret.data.replace(/\n/g,"/")+" ["+ret.rc+"]");
183 }
184 dim.wait("LID_CONTROL", "Open", 30000);
185
186 if (isClosed || isInconsistent)
187 dim.log("Lid open [%.1fs]".$((new Date()-tm)/1000));
188}
189
190function CloseLid()
191{
192 var isOpen = dim.state("LID_CONTROL").name=="Open";
193
194 var tm = new Date();
195
196 // Wait for lid to be open
197 if (isOpen)
198 {
199 if (dim.state("FTM_CONTROL").name=="TriggerOn")
200 {
201 dim.send("FTM_CONTROL/STOP_TRIGGER");
202 dim.wait("FTM_CONTROL", "Valid", 3000);
203 }
204
205 dim.log("Closing lid.");
206 dim.send("LID_CONTROL/CLOSE");
207 }
208 v8.timeout(30000, function() { if (dim.state("LID_CONTROL").name=="Closed" || dim.state("LID_CONTROL").name=="Inconsistent") return true; });
209 //dim.wait("LID_CONTROL", "Closed", 30000);
210 //dim.wait("LID_CONTROL", "Inconsistent", 30000);
211
212 if (isOpen)
213 dim.log("Lid closed [%.1fs]".$((new Date()-tm)/1000));
214}
215
216// ================================================================
217// Interrupt data taking in case of high currents
218// ================================================================
219dim.onchange['FEEDBACK'] = function(state)
220{
221 if ((state.name=="Critical" || state.name=="OnStandby") &&
222 (this.prev!="Critical" && this.prev!="OnStandby"))
223 {
224 console.out("Feedback state changed from "+this.prev+" to "+state.name+" [Main.js]");
225 irq = "RESCHEDULE";
226 }
227 this.prev=state.name;
228}
229
230// ================================================================
231// Code related to switching bias voltage on and off
232// ================================================================
233
234var service_feedback = new Subscription("FEEDBACK/CALIBRATED_CURRENTS");
235
236service_feedback.onchange = function(evt)
237{
238 if (!evt.data)
239 return;
240
241 if (this.ok==undefined)
242 return;
243
244 var Unom = evt.obj['U_nom'];
245 var Uov = evt.obj['U_ov'];
246 if (!Uov)
247 return;
248
249 var cnt = 0;
250 var avg = 0;
251 for (var i=0; i<320; i++)
252 {
253 // This is a fix for the channel with a shortcut
254 if (i==272)
255 continue;
256
257 var dU = Uov[i]-Unom;
258
259 // 0.022 corresponds to 1 DAC count (90V/4096)
260 if (Math.abs(dU)>0.033)
261 cnt++;
262
263 avg += dU;
264 }
265 avg /= 320;
266
267 this.ok = cnt<3;// || (this.last!=undefined && Math.abs(this.last-avg)<0.002);
268
269 console.out(" DeltaUov=%.3f (%.3f) [N(>0.033V)=%d]".$(avg, avg-this.last, cnt));
270
271 this.last = avg;
272}
273
274service_feedback.voltageOff = function()
275{
276 var state = dim.state("BIAS_CONTROL").name;
277
278 if (state=="Disconnected")
279 {
280 console.out(" Voltage off: bias crate disconnected!");
281 return;
282 }
283
284 // check of feedback has to be switched on
285 var isOn = state=="VoltageOn" || state=="Ramping";
286 if (isOn)
287 {
288 dim.log("Switching voltage off.");
289
290 if (dim.state("FTM_CONTROL").name=="TriggerOn")
291 {
292 dim.send("FTM_CONTROL/STOP_TRIGGER");
293 dim.wait("FTM_CONTROL", "Valid", 3000);
294 }
295
296 // Supress the possibility that the bias control is
297 // ramping and will reject the command to switch the
298 // voltage off
299 //dim.send("FEEDBACK/STOP");
300 //dim.wait("FEEDBACK", "Calibrated", 3000);
301
302 // Make sure we are not in Ramping anymore
303 //dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
304
305 // Switch voltage off
306 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
307 }
308
309 dim.wait("BIAS_CONTROL", "VoltageOff", 60000); // FIXME: 30000?
310 dim.wait("FEEDBACK", "Calibrated", 3000);
311
312 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
313 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
314
315 if (isOn)
316 dim.log("Voltage off.");
317}
318
319// DN: The name of the method voltageOn() in the context of the method
320// voltageOff() is a little bit misleading, since when voltageOff() returns
321// the caller can be sure the voltage is off, but when voltageOn() return
322// this is not the case, in the sense, that the caller can now take data.
323// instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
324// in order to safely take good-quality data.
325// This could lead to nasty bugs in the sense, that the second call might
326// be forgotten by somebody
327//
328// so I suggest to rename voltageOn() --> prepareVoltageOn()
329// waitForVoltageOn() stays as it is
330// and one creates a third method called:voltageOn() like this
331/* service_feedback.voltageOn = function()
332 * {
333 * this.prepareVoltageOn();
334 * this.waitForVoltageOn();
335 * }
336 *
337 * */
338// For convenience.
339
340service_feedback.voltageOn = function(ov)
341{
342 if (isNaN(ov))
343 ov = 1.1;
344
345 if (this.ov!=ov && dim.state("FEEDBACK").name=="InProgress") // FIXME: Warning, OnStandby, Critical if (ov<this.ov)
346 {
347 dim.log("Stoping feedback.");
348 if (dim.state("FTM_CONTROL").name=="TriggerOn")
349 {
350 dim.send("FTM_CONTROL/STOP_TRIGGER");
351 dim.wait("FTM_CONTROL", "Valid", 3000);
352 }
353
354 dim.send("FEEDBACK/STOP");
355 dim.wait("FEEDBACK", "Calibrated", 3000);
356
357 // Make sure we are not in Ramping anymore
358 dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
359 }
360
361 var isOff = dim.state("FEEDBACK").name=="Calibrated";
362 if (isOff)
363 {
364 dim.log("Switching voltage to Uov="+ov+"V.");
365
366 dim.send("FEEDBACK/START", ov);
367
368 // FIXME: We could miss "InProgress" if it immediately changes to "Warning"
369 // Maybe a dim.timeout state>8 ?
370 dim.wait("FEEDBACK", "InProgress", 45000);
371
372 this.ov = ov;
373 }
374
375 // Wait until voltage on
376 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
377}
378
379service_feedback.waitForVoltageOn = function()
380{
381 // Avoid output if condition is already fulfilled
382 dim.log("Waiting for voltage to be stable.");
383
384 function func()
385 {
386 if (irq || this.ok==true)
387 return true;
388 }
389
390 var now = new Date();
391
392 this.last = undefined;
393 this.ok = false;
394 v8.timeout(4*60000, func, this); // FIMXE: Remove 4!
395 this.ok = undefined;
396
397 if (irq)
398 dim.log("Waiting for stable voltage interrupted.");
399 else
400 dim.log("Voltage stable within limits");
401}
402
403// ================================================================
404// Function to shutdown the system
405// ================================================================
406
407function Shutdown(type)
408{
409 if (!type)
410 type = "default";
411
412 dim.log("Starting shutdown ["+type+"].");
413
414 var now1 = new Date();
415
416 var bias = dim.state("BIAS_CONTROL").name;
417 if (bias=="VoltageOn" || bias=="Ramping")
418 service_feedback.voltageOn(0);
419
420 CloseLid();
421
422 var now2 = new Date();
423
424 dim.send("DRIVE_CONTROL/PARK");
425
426 console.out("","Waiting for telescope to park. This may take a while.");
427
428 // FIXME: This might not work is the drive is already close to park position
429 //dim.wait("DRIVE_CONTROL", "Parking", 3000);
430
431 /*
432 // Check if DRS calibration is necessary
433 var diff = getTimeSinceLastDrsCalib();
434 if (diff>30 || diff==null)
435 {
436 doDrsCalibration("singlepe"); // will turn voltage off
437 if (irq)
438 break;
439 }*/
440
441 //take single pe run if required
442 if (type=="singlepe")
443 {
444 dim.log("Taking single-pe run.");
445
446 // The voltage must be on
447 service_feedback.voltageOn();
448 service_feedback.waitForVoltageOn();
449
450 // Before we can switch to 3000 we have to make the right DRS calibration
451 dim.log("Taking single p.e. run.");
452 while (!irq && !takeRun("single-pe", 10000));
453
454 /*
455 Maybe we need to send a trigger... but data runs contain pedestal triggers... so it should work in any case...
456 var customRun = function()
457 {
458 v8.sleep(500);//wait that configuration is set
459 dim.wait("FTM_CONTROL", "TriggerOn", 15000);
460 dim.send("FAD_CONTROL/SEND_SINGLE_TRIGGER");
461 dim.send("RATE_CONTROL/STOP");
462 dim.send("FTM_CONTROL/STOP_TRIGGER");
463 dim.wait("FTM_CONTROL", "Valid", 3000);
464 dim.send("FTM_CONTROL/ENABLE_TRIGGER", true);
465 dim.send("FTM_CONTROL/SET_TIME_MARKER_DELAY", 123);
466 dim.send("FTM_CONTROL/SET_THRESHOLD", -1, obs[sub].threshold);
467 v8.sleep(500);//wait that configuration is set
468 dim.send("FTM_CONTROL/START_TRIGGER");
469 dim.wait("FTM_CONTROL", "TriggerOn", 15000);
470 }*/
471 }
472
473 //wait until drive is in locked (after it reached park position)
474 dim.wait("DRIVE_CONTROL", "Locked", 150000);
475
476 //unlock drive if task was sleep
477 if (type=="sleep")
478 dim.send("DRIVE_CONTROL/UNLOCK");
479
480
481 // It is unclear what comes next, so we better switch off the voltage
482 service_feedback.voltageOff();
483
484 dim.log("Finishing shutdown.");
485
486 var now3 = new Date();
487
488 dim.send("FTM_CONTROL/STOP_TRIGGER");
489 dim.wait("FTM_CONTROL", "Valid", 3000);
490
491 if (bias!="Disconnected")
492 dim.wait("FEEDBACK", "Calibrated", 3000);
493
494 if (type!="sleep")
495 {
496 dim.send("BIAS_CONTROL/DISCONNECT");
497
498 var pwrctrl_state = dim.state("PWR_CONTROL").name;
499 if (pwrctrl_state=="SystemOn" ||
500 pwrctrl_state=="BiasOff" ||
501 pwrctrl_state=="DriveOn")
502 dim.send("PWR_CONTROL/TOGGLE_DRIVE");
503
504 dim.wait("BIAS_CONTROL", "Disconnected", 3000);
505 dim.wait("PWR_CONTROL", "DriveOff", 6000);
506 }
507
508 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
509 sub.get(5000); // FIXME: Proper error message in case of failure
510
511 var report = sub.get();
512
513 console.out("");
514 console.out("Shutdown procedure ["+type+"] seems to be finished...");
515 console.out(" "+new Date().toUTCString());
516 console.out(" Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
517 console.out(" Please check on the web cam that the park position was reached");
518 console.out(" and the telescope is not moving anymore.");
519 console.out(" Please check visually that the lid is really closed and");
520 console.out(" that the biasctrl really switched the voltage off.", "");
521 console.out(" DRIVE_CONTROL: "+dim.state("DRIVE_CONTROL").name);
522 console.out(" FEEDBACK: "+dim.state("FEEDBACK").name);
523 console.out(" FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
524 console.out(" BIAS_CONTROL: "+dim.state("BIAS_CONTROL").name);
525 console.out(" PWR_CONTROL: "+dim.state("PWR_CONTROL").name);
526 console.out("");
527 dim.log("Shutdown: end ["+(now2-now1)/1000+"s, "+(now3-now2)/1000+"s, "+(new Date()-now3)/1000+"s]");
528 console.out("");
529
530 sub.close();
531}
532
533
534// ================================================================
535// Function to set the system to sleep-mode
536// ================================================================
537// FIXME: do not repeat code from shutdown-function
538/*
539function GoToSleep()
540{
541 CloseLid();
542
543 var isArmed = dim.state("DRIVE_CONTROL").name=="Armed";
544 if (!isArmed)
545 {
546 dim.log("Drive not ready to move. -> send STOP");
547 dim.send("DRIVE_CONTROL/STOP");
548 dim.wait("DRIVE_CONTROL", "Armed", 5000);
549 }
550
551 dim.send("DRIVE_CONTROL/MOVE_TO 101 0");//park position
552 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
553 sub.get(5000); // FIXME: Proper error message in case of failure
554
555 function func()
556 {
557 var report = sub.get();
558
559 var zd = report.obj['Zd'];
560 var az = report.obj['Az'];
561
562 if (zd>100 && Math.abs(az)<1)
563 return true;
564
565 return undefined;
566 }
567
568 try { v8.timeout(150000, func); }
569 catch (e)
570 {
571 var p = sub.get();
572 dim.log('Park position not reached? Telescope at Zd='+p.obj['Zd']+' Az='+p.obj['Az']);
573 }
574 var p2 = sub.get();
575 dim.log('Telescope at Zd=%.1fdeg Az=%.1fdeg'.$(p2.obj['Zd'], p2.obj['Az']));
576 sub.close();
577}
578*/
579
580// ================================================================
581// Check datalogger subscriptions
582// ================================================================
583
584var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS");
585datalogger_subscriptions.get(3000, false);
586
587datalogger_subscriptions.check = function()
588{
589 var obj = this.get();
590 if (!obj.data)
591 throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available.");
592
593 var expected =
594 [
595 "AGILENT_CONTROL_24V/DATA",
596 "AGILENT_CONTROL_50V/DATA",
597 "AGILENT_CONTROL_80V/DATA",
598 "BIAS_CONTROL/CURRENT",
599 "BIAS_CONTROL/DAC",
600 "BIAS_CONTROL/NOMINAL",
601 "BIAS_CONTROL/VOLTAGE",
602 "DRIVE_CONTROL/POINTING_POSITION",
603 "DRIVE_CONTROL/SOURCE_POSITION",
604 "DRIVE_CONTROL/STATUS",
605 "DRIVE_CONTROL/TRACKING_POSITION",
606 "FAD_CONTROL/CONNECTIONS",
607 "FAD_CONTROL/DAC",
608 "FAD_CONTROL/DNA",
609 "FAD_CONTROL/DRS_RUNS",
610 "FAD_CONTROL/EVENTS",
611 "FAD_CONTROL/FEEDBACK_DATA",
612 "FAD_CONTROL/FILE_FORMAT",
613 "FAD_CONTROL/FIRMWARE_VERSION",
614 "FAD_CONTROL/INCOMPLETE",
615 "FAD_CONTROL/PRESCALER",
616 "FAD_CONTROL/REFERENCE_CLOCK",
617 "FAD_CONTROL/REGION_OF_INTEREST",
618 "FAD_CONTROL/RUNS",
619 "FAD_CONTROL/RUN_NUMBER",
620 "FAD_CONTROL/START_RUN",
621 "FAD_CONTROL/STATISTICS1",
622 "FAD_CONTROL/STATS",
623 "FAD_CONTROL/STATUS",
624 "FAD_CONTROL/TEMPERATURE",
625 "FEEDBACK/CALIBRATED_CURRENTS",
626 "FEEDBACK/CALIBRATION",
627 "FEEDBACK/CALIBRATION_R8",
628 "FEEDBACK/CALIBRATION_STEPS",
629/* "FEEDBACK/REFERENCE",*/
630 "FSC_CONTROL/CURRENT",
631 "FSC_CONTROL/HUMIDITY",
632 "FSC_CONTROL/TEMPERATURE",
633 "FSC_CONTROL/VOLTAGE",
634 "FTM_CONTROL/COUNTER",
635 "FTM_CONTROL/DYNAMIC_DATA",
636 "FTM_CONTROL/ERROR",
637 "FTM_CONTROL/FTU_LIST",
638 "FTM_CONTROL/PASSPORT",
639 "FTM_CONTROL/STATIC_DATA",
640 "FTM_CONTROL/TRIGGER_RATES",
641 "GPS_CONTROL/NEMA",
642 "SQM_CONTROL/DATA",
643 "LID_CONTROL/DATA",
644 "MAGIC_LIDAR/DATA",
645 "MAGIC_WEATHER/DATA",
646 "MCP/CONFIGURATION",
647 "PWR_CONTROL/DATA",
648 "RATE_CONTROL/THRESHOLD",
649 "RATE_SCAN/DATA",
650 "RATE_SCAN/PROCESS_DATA",
651 "TEMPERATURE/DATA",
652 "TIME_CHECK/OFFSET",
653 "TNG_WEATHER/DATA",
654 "TNG_WEATHER/DUST",
655 "PFMINI_CONTROL/DATA",
656 ];
657
658 function map(entry)
659 {
660 if (entry.length==0)
661 return undefined;
662
663 var rc = entry.split(',');
664 if (rc.length!=2)
665 throw new Error("Subscription list entry '"+entry+"' has wrong number of elements.");
666 return rc;
667 }
668
669 var list = obj.data.split('\n').map(map);
670 function check(name)
671 {
672 if (list.every(function(el){return el==undefined || el[0]!=name;}))
673 throw new Error("Subscription to '"+name+"' not available.");
674 }
675
676 expected.forEach(check);
677}
678
679
680
681// ================================================================
682// Crosscheck all states
683// ================================================================
684
685// ----------------------------------------------------------------
686// Do a standard startup to bring the system in into a well
687// defined state
688// ----------------------------------------------------------------
689include('scripts/Startup.js');
690
691// ================================================================
692// Code to monitor clock conditioner
693// ================================================================
694
695var sub_counter = new Subscription("FTM_CONTROL/COUNTER");
696sub_counter.onchange = function(evt)
697{
698 if (evt.qos>0 && evt.qos!=2 && evt.qos&0x100==0)
699 throw new Error("FTM reports: clock conditioner not locked.");
700}
701
702// ================================================================
703// Code related to monitoring the fad system
704// ================================================================
705
706// This code is here, because scripts/Startup.js needs the
707// same subscriptions... to be revised.
708var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
709var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
710var sub_startrun = new Subscription("FAD_CONTROL/START_RUN");
711sub_startrun.get(5000);
712
713include('scripts/takeRun.js');
714
715// ----------------------------------------------------------------
716// Check that everything we need is availabel to receive commands
717// (FIXME: Should that go to the general CheckState?)
718// ----------------------------------------------------------------
719//console.out("Checking send.");
720checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
721//console.out("Checking send: done");
722
723// ----------------------------------------------------------------
724// Bring feedback into the correct operational state
725// ----------------------------------------------------------------
726//console.out("Feedback init: start.");
727service_feedback.get(5000);
728
729// ----------------------------------------------------------------
730// Connect to the DRS_RUNS service
731// ----------------------------------------------------------------
732//console.out("Drs runs init: start.");
733
734var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
735sub_drsruns.get(5000);
736// FIXME: Check if the last DRS calibration was complete?
737
738function getTimeSinceLastDrsCalib()
739{
740 // ----- Time since last DRS Calibration [min] ------
741 var runs = sub_drsruns.get(0);
742 var diff = (new Date()-runs.time)/60000;
743
744 // Warning: 'roi=300' is a number which is not intrisically fixed
745 // but can change depending on the taste of the observers
746 var valid = runs.obj['run'][2]>0 && runs.obj['roi']==300;
747
748 if (valid)
749 dim.log("Last DRS calibration was %.1fmin ago".$(diff));
750 else
751 dim.log("No valid DRS calibration available.");
752
753 return valid ? diff : null;
754}
755
756// ----------------------------------------------------------------
757// Install interrupt handler
758// ----------------------------------------------------------------
759function handleIrq(cmd, args, time, user)
760{
761 console.out("Interrupt received:");
762 console.out(" IRQ: "+cmd);
763 console.out(" Time: "+time);
764 console.out(" User: "+user);
765
766 irq = cmd ? cmd : "stop";
767
768 // This will end a run in progress as if it where correctly stopped
769 if (dim.state("MCP").name=="TakingData")
770 dim.send("MCP/STOP");
771
772 // This will stop a rate scan in progress
773 if (dim.state("RATE_SCAN").name=="InProgress")
774 dim.send("RATE_SCAN/STOP");
775}
776
777dimctrl.setInterruptHandler(handleIrq);
778
779// ----------------------------------------------------------------
780// Make sure we will write files
781// ----------------------------------------------------------------
782dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
783
784// ----------------------------------------------------------------
785// Print some information for the user about the
786// expected first oberservation
787// ----------------------------------------------------------------
788var test = getObservation();
789if (test!=undefined)
790{
791 var n = new Date();
792 if (observations.length>0 && test==-1)
793 dim.log("First observation scheduled for "+observations[0].start.toUTCString()+" [id="+observations[0].id+"]");
794 if (test>=0 && test<observations.length)
795 dim.log("First observation should start immediately ["+observations[test].start.toUTCString()+", id="+observations[test].id+"]");
796 if (observations.length>0 && observations[0].start>n+12*3600*1000)
797 dim.log("No observations scheduled for the next 12 hours!");
798 if (observations.length==0)
799 dim.log("No observations scheduled!");
800}
801
802// ----------------------------------------------------------------
803// Start main loop
804// ----------------------------------------------------------------
805dim.log("Entering main loop.");
806console.out("");
807
808var run = -2; // getObservation never called
809var sub;
810var lastId;
811var nextId;
812var sun = Sun.horizon(-12);
813var system_on; // undefined
814
815function processIrq()
816{
817 if (!irq)
818 return false;
819
820 if (irq.toUpperCase()=="RESCHEDULE")
821 {
822 irq = undefined;
823 return false;
824 }
825
826 if (irq.toUpperCase()=="OFF")
827 {
828 service_feedback.voltageOff();
829 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
830 return true;
831 }
832
833 /*
834 if (irq.toUpperCase()=="STOP")
835 {
836 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
837 dim.send("MCP/STOP");
838 return true;
839 }*/
840
841 if (irq.toUpperCase()=="SHUTDOWN")
842 {
843 Shutdown();
844 return true;
845 }
846
847 dim.log("IRQ "+irq+" unhandled... stopping script.");
848 return true;
849}
850
851while (!processIrq())
852{
853 // Check if observation position is still valid
854 // If source position has changed, set run=0
855 var idxObs = getObservation();
856 if (idxObs===undefined)
857 break;
858
859 // we are still waiting for the first observation in the schedule
860 if (idxObs==-1)
861 {
862 // flag that the first observation will be in the future
863 run = -1;
864 v8.sleep(1000);
865 continue;
866 }
867
868 // Check if we have to take action do to sun-rise
869 var was_up = sun.isUp;
870 sun = Sun.horizon(-12);
871 if (!was_up && sun.isUp)
872 {
873 console.out("");
874 dim.log("Sun rise detected.... automatic shutdown initiated!");
875 // FIXME: State check?
876 Shutdown();
877 system_on = false;
878 continue;
879 }
880
881 // Current and next observation target
882 var obs = observations[idxObs];
883 var nextObs = observations[idxObs+1];
884
885 // Check if observation target has changed
886 if (lastId!=obs.id) // !Object.isEqual(obs, nextObs)
887 {
888 dim.log("Starting new observation ["+obs.start.toUTCString()+", id="+obs.id+"]");
889
890 // This is the first source, but we do not come from
891 // a scheduled 'START', so we have to check if the
892 // telescop is operational already
893 sub = 0;
894 if (run<0)
895 {
896 //Startup(); // -> Bias On/Off?, Lid open/closed?
897 //CloseLid();
898 }
899
900 // The first observation had a start-time in the past...
901 // In this particular case start with the last entry
902 // in the list of measurements
903 if (run==-2)
904 sub = obs.length-1;
905
906 run = 0;
907 lastId = obs.id;
908 }
909
910 //dim.log("DEBUG: Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
911 if (nextObs && nextId!=nextObs.id)
912 {
913 dim.log("Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
914 console.out("");
915 nextId = nextObs.id;
916 }
917
918 if (!nextObs && nextId)
919 {
920 dim.log("No further observation scheduled.");
921 console.out("");
922 nextId = undefined;
923 }
924
925 //if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
926 // throw Error("Last scheduled measurement must be a shutdown.");
927
928 // We are done with all measurement slots for this
929 // observation... wait for next observation
930 if (sub>=obs.length)
931 {
932 v8.sleep(1000);
933 continue;
934 }
935
936 if (system_on===false && obs[sub].task!="STARTUP")
937 {
938 v8.sleep(1000);
939 continue;
940 }
941
942 // Check if sun is still up... only DATA and */
943 if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN" || obs[sub].task=="RATESCAN2" ) && sun.isUp)
944 {
945 var now = new Date();
946 var remaining = (sun.set - now)/60000;
947 console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
948 v8.sleep(60000);
949 continue;
950 }
951
952
953 if (obs[sub].task!="IDLE" && (obs[sub].task!="DATA" && run>0))
954 dim.log("New task ["+obs[sub]+"]");
955
956 // FIXME: Maybe print a warning if Drive is on during day time!
957
958 // It is not ideal that we allow the drive to be on during day time, but
959 // otherwise it is difficult to allow e.g. the STARTUP at the beginning of the night
960 var power_states = sun.isUp || !system_on ? [ "DriveOff", "SystemOn" ] : [ "SystemOn" ];
961 var drive_states = sun.isUp || !system_on ? undefined : [ "Initialized", "Tracking", "OnTrack" ];
962
963 // A scheduled task was found, lets check if all servers are
964 // still only and in reasonable states. If this is not the case,
965 // something unexpected must have happend and the script is aborted.
966 //console.out(" Checking states [general]");
967 var table =
968 [
969 [ "TNG_WEATHER" ],
970 [ "MAGIC_WEATHER" ],
971 [ "CHAT" ],
972 [ "SMART_FACT" ],
973 [ "TEMPERATURE" ],
974 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
975 [ "FSC_CONTROL", [ "Connected" ] ],
976 [ "MCP", [ "Idle" ] ],
977 [ "TIME_CHECK", [ "Valid" ] ],
978 [ "PWR_CONTROL", power_states/*[ "SystemOn" ]*/ ],
979 [ "AGILENT_CONTROL_24V", [ "VoltageOn" ] ],
980 [ "AGILENT_CONTROL_50V", [ "VoltageOn" ] ],
981 [ "AGILENT_CONTROL_80V", [ "VoltageOn" ] ],
982 [ "BIAS_CONTROL", [ "VoltageOff", "VoltageOn", "Ramping" ] ],
983 [ "FEEDBACK", [ "Calibrated", "InProgress", "OnStandby", "Warning", "Critical" ] ],
984// [ "LID_CONTROL", [ "Open", "Closed" ] ],
985 [ "LID_CONTROL", [ "Open", "Closed", "Inconsistent" ] ],
986 [ "DRIVE_CONTROL", drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
987 [ "FTM_CONTROL", [ "Valid", "TriggerOn" ] ],
988 [ "FAD_CONTROL", [ "Connected", "RunInProgress" ] ],
989 [ "RATE_SCAN", [ "Connected" ] ],
990 [ "RATE_CONTROL", [ "Connected", "GlobalThresholdSet", "InProgress" ] ],
991 [ "GPS_CONTROL", [ "Locked" ] ],
992 [ "SQM_CONTROL", [ "Disconnected", "Connected", "Valid" ] ],
993 [ "PFMINI_CONTROL", [ "Disconnected", "Connected", "Receiving" ] ],
994 ];
995
996
997 if (!checkStates(table))
998 {
999 throw new Error("Something unexpected has happened. One of the servers "+
1000 "is in a state in which it should not be. Please,"+
1001 "try to find out what happened...");
1002 }
1003
1004 datalogger_subscriptions.check();
1005
1006 // If this is an observation which needs the voltage to be swicthed on
1007 // skip that if the voltage is not stable
1008 /*
1009 if (obs[sub].task=="DATA" || obs[sub].task=="RATESCAN")
1010 {
1011 var state = dim.state("FEEDBACK").name;
1012 if (state=="Warning" || state=="Critical" || state=="OnStandby")
1013 {
1014 v8.sleep(1000);
1015 continue;
1016 }
1017 }*/
1018
1019
1020 // Check if obs.task is one of the one-time-tasks
1021 switch (obs[sub].task)
1022 {
1023 case "IDLE":
1024 v8.sleep(5000);
1025 continue;
1026
1027 case "SUSPEND":
1028 case "SLEEP":
1029 Shutdown("sleep"); //GoToSleep();
1030
1031 dim.log("Task finished ["+obs[sub].task+"].");
1032 console.out("");
1033 sub++;
1034 continue;
1035
1036 case "STARTUP":
1037 CloseLid();
1038
1039 doDrsCalibration("startup"); // will switch the voltage off
1040
1041 if (irq)
1042 break;
1043
1044 service_feedback.voltageOn();
1045 service_feedback.waitForVoltageOn();
1046
1047 // Before we can switch to 3000 we have to make the right DRS calibration
1048 dim.log("Taking single p.e. run.");
1049 while (!irq && !takeRun("single-pe", 10000));
1050
1051 // It is unclear what comes next, so we better switch off the voltage
1052 service_feedback.voltageOff();
1053
1054 system_on = true;
1055 dim.log("Task finished [STARTUP]");
1056 console.out("");
1057 break;
1058
1059 case "SHUTDOWN":
1060 Shutdown("singlepe");
1061 system_on = false;
1062
1063 // FIXME: Avoid new observations after a shutdown until
1064 // the next startup (set run back to -2?)
1065 sub++;
1066 dim.log("Task finished [SHUTDOWN]");
1067 console.out("");
1068 //console.out(" Waiting for next startup.", "");
1069 continue;
1070
1071 case "DRSCALIB":
1072 doDrsCalibration("drscalib"); // will switch the voltage off
1073 dim.log("Task finished [DRSCALIB]");
1074 console.out("");
1075 break;
1076
1077 case "SINGLEPE":
1078 // The lid must be closes
1079 CloseLid();
1080
1081 // Check if DRS calibration is necessary
1082 var diff = getTimeSinceLastDrsCalib();
1083 if (diff>30 || diff==null)
1084 {
1085 doDrsCalibration("singlepe"); // will turn voltage off
1086 if (irq)
1087 break;
1088 }
1089
1090 // The voltage must be on
1091 service_feedback.voltageOn();
1092 service_feedback.waitForVoltageOn();
1093
1094 // Before we can switch to 3000 we have to make the right DRS calibration
1095 dim.log("Taking single p.e. run.");
1096 while (!irq && !takeRun("single-pe", 10000));
1097
1098 // It is unclear what comes next, so we better switch off the voltage
1099 service_feedback.voltageOff();
1100 dim.log("Task finished [SINGLE-PE]");
1101 console.out("");
1102 break;
1103
1104 case "OVTEST":
1105 var locked = dim.state("DRIVE_CONTROL").name=="Locked";
1106 if (!locked)
1107 dim.send("DRIVE_CONTROL/PARK");
1108
1109 dim.send("FEEDBACK/STOP");
1110
1111 // The lid must be closed
1112 CloseLid();
1113
1114 if (!locked)
1115 {
1116 //console.out("Waiting for telescope to park. This may take a while.");
1117 dim.wait("DRIVE_CONTROL", "Locked", 3000);
1118 dim.send("DRIVE_CONTROL/UNLOCK");
1119 }
1120
1121 // Check if DRS calibration is necessary
1122 var diff = getTimeSinceLastDrsCalib();
1123 if (diff>30 || diff==null)
1124 {
1125 doDrsCalibration("ovtest"); // will turn voltage off
1126 if (irq)
1127 break;
1128 }
1129
1130 // The voltage must be on
1131 service_feedback.voltageOn(0.4);
1132 service_feedback.waitForVoltageOn();
1133
1134 dim.log("Taking single p.e. run (0.4V)");
1135 while (!irq && !takeRun("single-pe", 10000));
1136
1137 for (var i=5; i<18 && !irq; i++)
1138 {
1139 dim.send("FEEDBACK/STOP");
1140 dim.wait("FEEDBACK", "Calibrated", 3000);
1141 dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
1142 dim.send("FEEDBACK/START", i*0.1);
1143 dim.wait("FEEDBACK", "InProgress", 45000);
1144 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
1145 service_feedback.waitForVoltageOn();
1146 dim.log("Taking single p.e. run ("+(i*0.1)+"V)");
1147 while (!irq && !takeRun("single-pe", 10000));
1148 }
1149
1150 // It is unclear what comes next, so we better switch off the voltage
1151 service_feedback.voltageOff();
1152 dim.log("Task finished [OVTEST]");
1153 console.out("");
1154 break;
1155
1156 case "RATESCAN":
1157 var tm1 = new Date();
1158
1159 // This is a workaround to make sure that we really catch
1160 // the new OnTrack state later and not the old one
1161 dim.send("DRIVE_CONTROL/STOP");
1162 dim.wait("DRIVE_CONTROL", "Initialized", 15000);
1163
1164 // The lid must be open
1165 OpenLid();
1166
1167 // Switch the voltage to a reduced level (Ubd)
1168 service_feedback.voltageOn(0);
1169
1170 if (obs[sub].source != null) // undefined != null -> false
1171 {
1172 dim.log("Pointing telescope to '"+obs[sub].source+"'.");
1173 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
1174 }
1175 else
1176 {
1177 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
1178 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
1179 }
1180
1181 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1182
1183 // Now tracking stable, switch voltage to nominal level and wait
1184 // for stability.
1185 service_feedback.voltageOn();
1186 service_feedback.waitForVoltageOn();
1187
1188 if (!irq)
1189 {
1190 dim.log("Starting calibration.");
1191
1192 // Calibration (2% of 20')
1193 while (!irq)
1194 {
1195 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s
1196 continue;
1197 //if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s
1198 // continue;
1199 break;
1200 }
1201
1202 var tm2 = new Date();
1203
1204 dim.log("Starting ratescan.");
1205
1206 //set reference to whole camera (in case it was changed)
1207 dim.send("RATE_SCAN/SET_REFERENCE_CAMERA");
1208 // Start rate scan
1209 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10, "default");
1210
1211 // Lets wait if the ratescan really starts... this might take a few
1212 // seconds because RATE_SCAN configures the ftm and is waiting for
1213 // it to be configured.
1214 dim.wait("RATE_SCAN", "InProgress", 10000);
1215 dim.wait("RATE_SCAN", "Connected", 2700000);
1216
1217 // Here one could implement a watchdog for the feedback as well, but what is the difference
1218 // whether finally one has to find out if the feedback was in the correct state
1219 // or the ratescan was interrupted?
1220
1221 // this line is actually some kind of hack.
1222 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
1223 // So I decided to put this line here as a kind of patchwork....
1224 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
1225
1226 dim.log("Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
1227 }
1228
1229 dim.log("Task finished [RATESCAN]");
1230 console.out("");
1231 break; // case "RATESCAN"
1232
1233 case "RATESCAN2":
1234 var tm1 = new Date();
1235
1236 // This is a workaround to make sure that we really catch
1237 // the new OnTrack state later and not the old one
1238 dim.send("DRIVE_CONTROL/STOP");
1239 dim.wait("DRIVE_CONTROL", "Initialized", 15000);
1240
1241 if (obs[sub].rstype=="dark-bias-off")
1242 service_feedback.voltageOff();
1243 else
1244 {
1245 // Switch the voltage to a reduced level (Ubd)
1246 var bias = dim.state("BIAS_CONTROL").name;
1247 if (bias=="VoltageOn" || bias=="Ramping")
1248 service_feedback.voltageOn(0);
1249 }
1250
1251 // Open the lid if required
1252 if (!obs[sub].lidclosed)
1253 OpenLid();
1254 else
1255 CloseLid();
1256
1257 // track source/position or move to position
1258 if (obs[sub].lidclosed)
1259 {
1260 dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az);
1261 dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az);
1262 v8.sleep(3000);
1263 dim.wait("DRIVE_CONTROL", "Initialized", 150000); // 110s for turning and 30s for stabilizing
1264 }
1265 else
1266 {
1267 if (obs[sub].source != null) // undefined != null -> false
1268 {
1269 dim.log("Pointing telescope to '"+obs[sub].source+"'.");
1270 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
1271 }
1272 else
1273 {
1274 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
1275 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
1276 }
1277
1278 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1279 }
1280
1281 // Now tracking stable, switch voltage to nominal level and wait
1282 // for stability.
1283 if (obs[sub].rstype!="dark-bias-off")
1284 {
1285 service_feedback.voltageOn();
1286 service_feedback.waitForVoltageOn();
1287 }
1288
1289 if (!irq)
1290 {
1291 var tm2 = new Date();
1292
1293 dim.log("Starting ratescan 2/1 ["+obs[sub].rstype+"]");
1294
1295 //set reference to whole camera (in case it was changed)
1296 dim.send("RATE_SCAN/SET_REFERENCE_CAMERA");
1297 // Start rate scan
1298 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 300, 20, obs[sub].rstype);
1299
1300 // Lets wait if the ratescan really starts... this might take a few
1301 // seconds because RATE_SCAN configures the ftm and is waiting for
1302 // it to be configured.
1303 dim.wait("RATE_SCAN", "InProgress", 10000);
1304 //FIXME: discuss what best value is here
1305 dim.wait("RATE_SCAN", "Connected", 2700000);//45min
1306 //dim.wait("RATE_SCAN", "Connected", 1200000);//3.3h
1307
1308 // Here one could implement a watchdog for the feedback as well, but what is the difference
1309 // whether finally one has to find out if the feedback was in the correct state
1310 // or the ratescan was interrupted?
1311
1312 // this line is actually some kind of hack.
1313 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
1314 // So I decided to put this line here as a kind of patchwork....
1315 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
1316
1317 dim.log("Ratescan 2/1 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
1318 }
1319
1320 if (!irq)
1321 {
1322 var tm2 = new Date();
1323
1324 dim.log("Starting ratescan 2/2 ["+obs[sub].rstype+"]");
1325
1326 // Start rate scan
1327 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 300, 1000, 100, obs[sub].rstype);
1328
1329 // Lets wait if the ratescan really starts... this might take a few
1330 // seconds because RATE_SCAN configures the ftm and is waiting for
1331 // it to be configured.
1332 dim.wait("RATE_SCAN", "InProgress", 10000);
1333 dim.wait("RATE_SCAN", "Connected", 2700000);
1334
1335 // Here one could implement a watchdog for the feedback as well, but what is the difference
1336 // whether finally one has to find out if the feedback was in the correct state
1337 // or the ratescan was interrupted?
1338
1339 // this line is actually some kind of hack.
1340 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
1341 // So I decided to put this line here as a kind of patchwork....
1342 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
1343
1344 dim.log("Ratescan 2/2 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
1345 }
1346
1347 dim.log("Task finished [RATESCAN2]");
1348 console.out("");
1349 break; // case "RATESCAN2"
1350
1351 case "CUSTOM":
1352
1353 // This is a workaround to make sure that we really catch
1354 // the new OnTrack state later and not the old one
1355 dim.send("DRIVE_CONTROL/STOP");
1356 dim.wait("DRIVE_CONTROL", "Initialized", 15000);
1357
1358 // Ramp bias if needed
1359 if (!obs[sub].biason)
1360 service_feedback.voltageOff();
1361 else
1362 {
1363 // Switch the voltage to a reduced level (Ubd)
1364 var bias = dim.state("BIAS_CONTROL").name;
1365 if (bias=="VoltageOn" || bias=="Ramping")
1366 service_feedback.voltageOn(0);
1367 }
1368 // Close lid
1369 CloseLid();
1370
1371 // Move to position (zd/az)
1372 dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az);
1373 dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az);
1374 v8.sleep(3000);
1375 dim.wait("DRIVE_CONTROL", "Initialized", 150000); // 110s for turning and 30s for stabilizing
1376
1377 // Now tracking stable, switch voltage to nominal level and wait
1378 // for stability.
1379 if (obs[sub].biason)
1380 {
1381 service_feedback.voltageOn();
1382 service_feedback.waitForVoltageOn();
1383 }
1384
1385 if (!irq)
1386 {
1387 dim.log("Taking custom run with time "+obs[sub].time+"s, threshold="+obs[sub].threshold+", biason="+obs[sub].biason);
1388
1389 var customRun = function()
1390 {
1391 v8.sleep(500);//wait that configuration is set
1392 dim.wait("FTM_CONTROL", "TriggerOn", 15000);
1393 dim.send("FAD_CONTROL/SEND_SINGLE_TRIGGER");
1394 dim.send("RATE_CONTROL/STOP");
1395 dim.send("FTM_CONTROL/STOP_TRIGGER");
1396 dim.wait("FTM_CONTROL", "Valid", 3000);
1397 dim.send("FTM_CONTROL/ENABLE_TRIGGER", true);
1398 dim.send("FTM_CONTROL/SET_TIME_MARKER_DELAY", 123);
1399 dim.send("FTM_CONTROL/SET_THRESHOLD", -1, obs[sub].threshold);
1400 v8.sleep(500);//wait that configuration is set
1401 dim.send("FTM_CONTROL/START_TRIGGER");
1402 dim.wait("FTM_CONTROL", "TriggerOn", 15000);
1403 }
1404
1405 takeRun("custom", -1, obs[sub].time, customRun);
1406 }
1407 dim.log("Task finished [CUSTOM].");
1408 dim.log("");
1409 break; // case "CUSTOM"
1410
1411 case "DATA":
1412
1413 // ========================== case "DATA" ============================
1414 /*
1415 if (Sun.horizon("FACT").isUp)
1416 {
1417 console.out(" SHUTDOWN","");
1418 Shutdown();
1419 console.out(" Exit forced due to broken schedule", "");
1420 exit();
1421 }
1422 */
1423
1424 // Calculate remaining time for this observation in minutes
1425 var remaining = nextObs==undefined ? 0 : (nextObs.start-new Date())/60000;
1426 //dim.log("DEBUG: remaining: "+remaining+" nextObs="+nextObs+" start="+nextObs.start);
1427
1428 // ------------------------------------------------------------
1429
1430 dim.log("Run count "+run+" [remaining "+parseInt(remaining)+"min]");
1431
1432 // ----- Time since last DRS Calibration [min] ------
1433 var diff = getTimeSinceLastDrsCalib();
1434
1435 // Changine pointing position and take calibration...
1436 // ...every four runs (every ~20min)
1437 // ...if at least ten minutes of observation time are left
1438 // ...if this is the first run on the source
1439 var point = (run%4==0 && remaining>10 && !obs[sub].orbit) || run==0; // undefined==null -> true!
1440
1441 // Take DRS Calib...
1442 // ...every four runs (every ~20min)
1443 // ...at last every two hours
1444 // ...when DRS temperature has changed by more than 2deg (?)
1445 // ...when more than 15min of observation are left
1446 // ...no drs calibration was done yet
1447 var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null;
1448
1449 if (point)
1450 {
1451 // Switch the voltage to a reduced voltage level
1452 service_feedback.voltageOn(0);
1453
1454 // Change wobble position every four runs,
1455 // start with alternating wobble positions each day
1456 var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
1457 var angle = obs[sub].angle == null ? Math.random()*360 : obs[sub].angle;
1458
1459 if (obs[sub].orbit) // != undefined, != null, != 0
1460 dim.log("Pointing telescope to '"+obs[sub].source+"' [orbit="+obs[sub].orbit+"min, angle="+angle+"]");
1461 else
1462 dim.log("Pointing telescope to '"+obs[sub].source+"' [wobble="+wobble+"]");
1463
1464 // This is a workaround to make sure that we really catch
1465 // the new OnTrack state later and not the old one
1466 dim.send("DRIVE_CONTROL/STOP");
1467 dim.wait("DRIVE_CONTROL", "Initialized", 15000);
1468
1469 if (obs[sub].orbit) // != undefined, != null, != 0
1470 dim.send("DRIVE_CONTROL/TRACK_ORBIT", angle, obs[sub].orbit, obs[sub].source);
1471 else
1472 dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
1473
1474 // Do we have to check if the telescope is really moving?
1475 // We can cross-check the SOURCE service later
1476 }
1477
1478 if (drscal)
1479 {
1480 doDrsCalibration("data"); // will turn voltage off
1481
1482 // Now we switch on the voltage and a significant amount of
1483 // time has been passed, so do the check again.
1484 sun = Sun.horizon(-12);
1485 if (!was_up && sun.isUp)
1486 {
1487 dim.log("Sun rise detected....");
1488 continue;
1489 }
1490 }
1491
1492 if (irq)
1493 continue;
1494
1495 OpenLid();
1496
1497 // This is now th right time to wait for th drive to be stable
1498 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1499
1500 // Now check the voltage... (do not start a lot of stuff just to do nothing)
1501 var state = dim.state("FEEDBACK").name;
1502 if (state=="Warning" || state=="Critical" || state=="OnStandby")
1503 {
1504 v8.sleep(60000);
1505 continue;
1506 }
1507
1508 // Now we are 'OnTrack', so we can ramp to nominal voltage
1509 // and wait for the feedback to get stable
1510 service_feedback.voltageOn();
1511 service_feedback.waitForVoltageOn();
1512
1513 // If pointing had changed, do calibration
1514 if (!irq && point)
1515 {
1516 dim.log("Starting calibration.");
1517
1518 // Calibration (2% of 20')
1519 while (!irq)
1520 {
1521 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s
1522 continue;
1523// if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s
1524// continue;
1525 break;
1526 }
1527 }
1528
1529 //console.out(" Taking data: start [5min]");
1530
1531 // FIXME: What do we do if during calibration something has happened
1532 // e.g. drive went to ERROR? Maybe we have to check all states again?
1533
1534 var twilight = Sun.horizon(-16).isUp;
1535
1536 if (twilight)
1537 {
1538 for (var i=0; i<5 && !irq; i++)
1539 takeRun("data", -1, 60); // Take data (1min)
1540 }
1541 else
1542 {
1543 var len = 300;
1544 while (!irq && len>15)
1545 {
1546 var time = new Date();
1547 if (takeRun("data", -1, len)) // Take data (5min)
1548 break;
1549
1550 len -= parseInt((new Date()-time)/1000);
1551 }
1552 }
1553
1554 //console.out(" Taking data: done");
1555 run++;
1556
1557 continue; // case "DATA"
1558 }
1559
1560 if (nextObs!=undefined && sub==obs.length-1)
1561 dim.log("Next observation will start at "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
1562
1563 sub++;
1564}
1565
1566sub_drsruns.close();
1567
1568dim.log("Left main loop [irq="+irq+"]");
1569
1570// ================================================================
1571// Comments and ToDo goes here
1572// ================================================================
1573
1574// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
1575// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
Note: See TracBrowser for help on using the repository browser.