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

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