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

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