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

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