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

Last change on this file since 17921 was 17673, checked in by tbretz, 11 years ago
Adapted to have three agilentctrl running instead of one.
File size: 37.5 KB
Line 
1/**
2 * @fileOverview This file has functions related to documenting JavaScript.
3 * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
4 */
5'use strict';
6
7dim.log("Start: "+__FILE__+" ["+__DATE__+"]");
8
9// This should be set in dimctrl.rc as JavaScript.schedule-database.
10// It is sent together with the script to the dimserver.
11// If started directly, it has to be set after the command:
12//
13// .js scripts/Main.js schedule-database=...
14//
15if (!$['schedule-database'])
16 throw new Error("Environment 'schedule-database' not set!");
17
18//dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
19
20// ================================================================
21// Code related to the schedule
22// ================================================================
23
24//this is just the class implementation of 'Observation'
25include('scripts/Observation_class.js');
26include('scripts/getSchedule.js');
27
28var observations = [ ];
29
30// Get the observation scheduled for 'now' from the table and
31// return its index
32function getObservation(now)
33{
34 if (now==undefined)
35 now = new Date();
36
37 if (isNaN(now.valueOf()))
38 throw new Error("Date argument in getObservation invalid.");
39
40 observations = getSchedule();
41
42 for (var i=0; i<observations.length; i++)
43 if (now<observations[i].start)
44 return i-1;
45
46 return observations.length-1;
47}
48
49// ================================================================
50// Code to check whether observation is allowed
51// ================================================================
52/*
53function currentEst(source)
54{
55 var moon = new Moon();
56 if (!moon.isUp)
57 return 7.7;
58
59 var dist = Sky.dist(moon, source);
60
61 var alt = 90-moon.toLocal().zd;
62
63 var lc = dist*alt*pow(Moon.disk(), 6)/360/360;
64
65 var cur = 7.7+4942*lc;
66
67 return cur;
68}
69
70function thresholdEst(source) // relative threshold (ratio)
71{
72 // Assumption:
73 // atmosphere is 70km, shower taks place after 60km, earth radius 6400km
74 // just using the cosine law
75 // This fits very well with MC results: See Roger Firpo, p.45
76 // "Study of the MAGIC telescope sensitivity for Large Zenith Angle observations"
77
78 var c = Math.cos(Math.Pi-source.zd);
79 var ratio = (10*sqrt(409600*c*c+9009) + 6400*c - 60)/10;
80
81 // assumption: Energy threshold increases linearily with current
82 // assumption: Energy threshold increases linearily with distance
83
84 return ratio*currentEst(source)/7.7;
85}
86*/
87
88// ================================================================
89// Code to perform the DRS calib sequence
90// ================================================================
91
92var irq;
93
94function doDrsCalibration(where)
95{
96 dim.log("Starting DRS calibration ["+where+"]");
97
98 service_feedback.voltageOff();
99
100 var tm = new Date();
101
102 while (!irq)
103 {
104 dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
105 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
106 continue;
107
108 if (irq || !takeRun("drs-gain", 1000)) // 40 / 20s (50Hz)
109 continue;
110
111 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz)
112 continue;
113
114 break;
115 }
116
117 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
118
119 while (!irq && !takeRun("drs-pedestal", 1000)); // 40 / 20s (50Hz)
120 while (!irq && !takeRun("drs-time", 1000)); // 40 / 20s (50Hz)
121
122 while (!irq)
123 {
124 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
125 if (takeRun("pedestal", 1000)) // 40 / 10s (80Hz)
126 break;
127 }
128
129 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
130
131 while (!irq && !takeRun("pedestal", 1000)); // 40 / 10s (80Hz)
132 // -----------
133 // 4'40 / 2'00
134
135 if (irq)
136 dim.log("DRS calibration interrupted [%.1fs]".$((new Date()-tm)/1000));
137 else
138 dim.log("DRS calibration done [%.1fs]".$((new Date()-tm)/1000));
139}
140
141// ================================================================
142// Code related to the lid
143// ================================================================
144
145function OpenLid()
146{
147 /*
148 while (Sun.horizon(-13).isUp)
149 {
150 var now = new Date();
151 var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
152 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
153 v8.sleep(60000);
154 }*/
155
156 var isClosed = dim.state("LID_CONTROL").name=="Closed";
157
158 var tm = new Date();
159
160 // Wait for lid to be open
161 if (isClosed)
162 {
163 dim.log("Opening lid");
164 dim.send("LID_CONTROL/OPEN");
165 }
166 dim.wait("LID_CONTROL", "Open", 30000);
167
168 if (isClosed)
169 dim.log("Lid open [%.1fs]".$((new Date()-tm)/1000));
170}
171
172function CloseLid()
173{
174 var isOpen = dim.state("LID_CONTROL").name=="Open";
175
176 var tm = new Date();
177
178 // Wait for lid to be open
179 if (isOpen)
180 {
181 if (dim.state("FTM_CONTROL").name=="TriggerOn")
182 {
183 dim.send("FTM_CONTROL/STOP_TRIGGER");
184 dim.wait("FTM_CONTROL", "Valid", 3000);
185 }
186
187 dim.log("Closing lid.");
188 dim.send("LID_CONTROL/CLOSE");
189 }
190 dim.wait("LID_CONTROL", "Closed", 30000);
191
192 if (isOpen)
193 dim.log("Lid closed [%.1fs]".$((new Date()-tm)/1000));
194}
195
196// ================================================================
197// Code related to switching bias voltage on and off
198// ================================================================
199
200var service_feedback = new Subscription("FEEDBACK/CALIBRATED_CURRENTS");
201
202service_feedback.onchange = function(evt)
203{
204 if (!evt.data)
205 return;
206
207 var Unom = evt.obj['U_nom'];
208 var Uov = evt.obj['U_ov'];
209 if (!Uov)
210 return;
211
212 var cnt = 0;
213 var avg = 0;
214 for (var i=0; i<320; i++)
215 {
216 var dU = Uov[i]-Unom;
217
218 // 0.022 corresponds to 1 DAC count (90V/4096)
219 if (Math.abs(dU)>0.033)
220 cnt++;
221
222 avg += dU;
223 }
224 avg /= 320;
225
226 if (this.ok==undefined)
227 return;
228
229
230 this.ok = cnt<3;// || (this.last!=undefined && Math.abs(this.last-avg)<0.002);
231
232 console.out(" DeltaUov=%.3f (%.3f) [N(>0.033V)=%d]".$(avg, avg-this.last, cnt));
233
234 this.last = avg;
235}
236
237service_feedback.voltageOff = function()
238{
239 var state = dim.state("BIAS_CONTROL").name;
240
241 if (state=="Disconnected")
242 {
243 console.out(" Voltage off: bias crate disconnected!");
244 return;
245 }
246
247 // check of feedback has to be switched on
248 var isOn = state=="VoltageOn" || state=="Ramping";
249 if (isOn)
250 {
251 dim.log("Switching voltage off.");
252
253 if (dim.state("FTM_CONTROL").name=="TriggerOn")
254 {
255 dim.send("FTM_CONTROL/STOP_TRIGGER");
256 dim.wait("FTM_CONTROL", "Valid", 3000);
257 }
258
259 // Supress the possibility that the bias control is
260 // ramping and will reject the command to switch the
261 // voltage off
262 //dim.send("FEEDBACK/STOP");
263 //dim.wait("FEEDBACK", "Calibrated", 3000);
264
265 // Make sure we are not in Ramping anymore
266 //dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
267
268 // Switch voltage off
269 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
270 }
271
272 dim.wait("BIAS_CONTROL", "VoltageOff", 60000); // FIXME: 30000?
273 dim.wait("FEEDBACK", "Calibrated", 3000);
274
275 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
276 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
277
278 if (isOn)
279 dim.log("Voltage off.");
280}
281
282// DN: The name of the method voltageOn() in the context of the method
283// voltageOff() is a little bit misleading, since when voltageOff() returns
284// the caller can be sure the voltage is off, but when voltageOn() return
285// this is not the case, in the sense, that the caller can now take data.
286// instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
287// in order to safely take good-quality data.
288// This could lead to nasty bugs in the sense, that the second call might
289// be forgotten by somebody
290//
291// so I suggest to rename voltageOn() --> prepareVoltageOn()
292// waitForVoltageOn() stays as it is
293// and one creates a third method called:voltageOn() like this
294/* service_feedback.voltageOn = function()
295 * {
296 * this.prepareVoltageOn();
297 * this.waitForVoltageOn();
298 * }
299 *
300 * */
301// For convenience.
302
303service_feedback.voltageOn = function(ov)
304{
305 if (isNaN(ov))
306 ov = 1.1;
307
308 if (this.ov!=ov && dim.state("FEEDBACK").name=="InProgress")
309 {
310 dim.log("Stoping feedback.");
311 if (dim.state("FTM_CONTROL").name=="TriggerOn")
312 {
313 dim.send("FTM_CONTROL/STOP_TRIGGER");
314 dim.wait("FTM_CONTROL", "Valid", 3000);
315 }
316
317 dim.send("FEEDBACK/STOP");
318 dim.wait("FEEDBACK", "Calibrated", 3000);
319
320 // Make sure we are not in Ramping anymore
321 dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
322 }
323
324 var isOff = dim.state("FEEDBACK").name=="Calibrated";
325 if (isOff)
326 {
327 dim.log("Switching voltage to Uov="+ov+"V.");
328
329 dim.send("FEEDBACK/START", ov);
330 dim.wait("FEEDBACK", "InProgress", 45000);
331
332 this.ov = ov;
333 }
334
335 // Wait until voltage on
336 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
337}
338
339service_feedback.waitForVoltageOn = function()
340{
341 // Avoid output if condition is already fulfilled
342 dim.log("Waiting for voltage to be stable.");
343
344 function func()
345 {
346 if (irq || this.ok==true)
347 return true;
348 }
349
350 var now = new Date();
351
352 this.last = undefined;
353 this.ok = false;
354 v8.timeout(4*60000, func, this); // FIMXE: Remove 4!
355 this.ok = undefined;
356
357 if (irq)
358 dim.log("Waiting for stable voltage interrupted.");
359 else
360 dim.log("Voltage stable within limits");
361}
362
363// ================================================================
364// Function to shutdown the system
365// ================================================================
366
367function Shutdown()
368{
369 dim.log("Starting shutdown.");
370
371 var now1 = new Date();
372
373 var bias = dim.state("BIAS_CONTROL").name;
374 if (bias=="VoltageOn" || bias=="Ramping")
375 service_feedback.voltageOn(0);
376
377 CloseLid();
378
379 var now2 = new Date();
380
381 dim.send("DRIVE_CONTROL/PARK");
382
383 console.out("","Waiting for telescope to park. This may take a while.");
384
385 // FIXME: This might not work is the drive is already close to park position
386 dim.wait("DRIVE_CONTROL", "Locked", 3000);
387
388 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
389 sub.get(5000); // FIXME: Proper error message in case of failure
390
391 function func()
392 {
393 var report = sub.get();
394
395 var zd = report.obj['Zd'];
396 var az = report.obj['Az'];
397
398 if (zd>100 && Math.abs(az)<1)
399 return true;
400
401 return undefined;
402 }
403
404 try { v8.timeout(150000, func); }
405 catch (e)
406 {
407 var p = sub.get();
408 dim.log('Park position not reached? Telescope at Zd='+p.obj['Zd']+' Az='+p.obj['Az']);
409 }
410
411 /*
412 // Check if DRS calibration is necessary
413 var diff = getTimeSinceLastDrsCalib();
414 if (diff>30 || diff==null)
415 {
416 doDrsCalibration("singlepe"); // will turn voltage off
417 if (irq)
418 break;
419 }*/
420
421 dim.log("Taking single-pe run.");
422
423 // The voltage must be on
424 service_feedback.voltageOn();
425 service_feedback.waitForVoltageOn();
426
427 // Before we can switch to 3000 we have to make the right DRS calibration
428 dim.log("Taking single p.e. run.");
429 while (!irq && !takeRun("single-pe", 10000));
430
431 // It is unclear what comes next, so we better switch off the voltage
432 service_feedback.voltageOff();
433
434 dim.log("Finishing shutdown.");
435
436 var now3 = new Date();
437
438 //dim.send("FEEDBACK/STOP");
439 dim.send("FTM_CONTROL/STOP_TRIGGER");
440
441 dim.wait("FTM_CONTROL", "Valid", 3000);
442 dim.wait("FEEDBACK", "Calibrated", 3000);
443
444 dim.send("BIAS_CONTROL/DISCONNECT");
445 dim.wait("BIAS_CONTROL", "Disconnected", 3000);
446
447 var report = sub.get();
448
449 console.out("");
450 console.out("Shutdown procedure seems to be finished...");
451 console.out(" "+new Date().toUTCString());
452 console.out(" Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
453 console.out(" Please check on the web cam that the park position was reached");
454 console.out(" and the telescope is not moving anymore.");
455 console.out(" Please check visually that the lid is really closed and");
456 console.out(" that the biasctrl really switched the voltage off.", "");
457 console.out(" DRIVE_CONTROL: "+dim.state("DRIVE_CONTROL").name);
458 console.out(" FEEDBACK: "+dim.state("FEEDBACK").name);
459 console.out(" FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
460 console.out(" BIAS_CONTROL: "+dim.state("BIAS_CONTROL").name);
461 console.out("");
462 dim.log("Shutdown: end ["+(now2-now1)/1000+"s, "+(now3-now2)/1000+"s, "+(new Date()-now3)/1000+"s]");
463 console.out("");
464
465 sub.close();
466}
467
468// ================================================================
469// Check datalogger subscriptions
470// ================================================================
471
472var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS");
473datalogger_subscriptions.get(3000, false);
474
475datalogger_subscriptions.check = function()
476{
477 var obj = this.get();
478 if (!obj.data)
479 throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available.");
480
481 var expected =
482 [
483 "AGILENT_CONTROL_24V/DATA",
484 "AGILENT_CONTROL_50V/DATA",
485 "AGILENT_CONTROL_80V/DATA",
486 "BIAS_CONTROL/CURRENT",
487 "BIAS_CONTROL/DAC",
488 "BIAS_CONTROL/NOMINAL",
489 "BIAS_CONTROL/VOLTAGE",
490 "DRIVE_CONTROL/POINTING_POSITION",
491 "DRIVE_CONTROL/SOURCE_POSITION",
492 "DRIVE_CONTROL/STATUS",
493 "DRIVE_CONTROL/TRACKING_POSITION",
494 "FAD_CONTROL/CONNECTIONS",
495 "FAD_CONTROL/DAC",
496 "FAD_CONTROL/DNA",
497 "FAD_CONTROL/DRS_RUNS",
498 "FAD_CONTROL/EVENTS",
499 "FAD_CONTROL/FEEDBACK_DATA",
500 "FAD_CONTROL/FILE_FORMAT",
501 "FAD_CONTROL/FIRMWARE_VERSION",
502 "FAD_CONTROL/INCOMPLETE",
503 "FAD_CONTROL/PRESCALER",
504 "FAD_CONTROL/REFERENCE_CLOCK",
505 "FAD_CONTROL/REGION_OF_INTEREST",
506 "FAD_CONTROL/RUNS",
507 "FAD_CONTROL/RUN_NUMBER",
508 "FAD_CONTROL/START_RUN",
509 "FAD_CONTROL/STATISTICS1",
510 "FAD_CONTROL/STATS",
511 "FAD_CONTROL/STATUS",
512 "FAD_CONTROL/TEMPERATURE",
513 "FEEDBACK/CALIBRATED_CURRENTS",
514 "FEEDBACK/CALIBRATION",
515 "FEEDBACK/CALIBRATION_R8",
516 "FEEDBACK/CALIBRATION_STEPS",
517/* "FEEDBACK/REFERENCE",*/
518 "FSC_CONTROL/CURRENT",
519 "FSC_CONTROL/HUMIDITY",
520 "FSC_CONTROL/TEMPERATURE",
521 "FSC_CONTROL/VOLTAGE",
522 "FTM_CONTROL/COUNTER",
523 "FTM_CONTROL/DYNAMIC_DATA",
524 "FTM_CONTROL/ERROR",
525 "FTM_CONTROL/FTU_LIST",
526 "FTM_CONTROL/PASSPORT",
527 "FTM_CONTROL/STATIC_DATA",
528 "FTM_CONTROL/TRIGGER_RATES",
529 "GPS_CONTROL/NEMA",
530 "LID_CONTROL/DATA",
531 "MAGIC_LIDAR/DATA",
532 "MAGIC_WEATHER/DATA",
533 "MCP/CONFIGURATION",
534 "PWR_CONTROL/DATA",
535 "RATE_CONTROL/THRESHOLD",
536 "RATE_SCAN/DATA",
537 "RATE_SCAN/PROCESS_DATA",
538 "TEMPERATURE/DATA",
539 "TIME_CHECK/OFFSET",
540 "TNG_WEATHER/DATA",
541 "TNG_WEATHER/DUST",
542 ];
543
544 function map(entry)
545 {
546 if (entry.length==0)
547 return undefined;
548
549 var rc = entry.split(',');
550 if (rc.length!=2)
551 throw new Error("Subscription list entry '"+entry+"' has wrong number of elements.");
552 return rc;
553 }
554
555 var list = obj.data.split('\n').map(map);
556
557 function check(name)
558 {
559 if (list.every(function(el){return el==undefined || el[0]!=name;}))
560 throw new Error("Subscription to '"+name+"' not available.");
561 }
562
563 expected.forEach(check);
564}
565
566
567
568// ================================================================
569// Crosscheck all states
570// ================================================================
571
572// ----------------------------------------------------------------
573// Do a standard startup to bring the system in into a well
574// defined state
575// ----------------------------------------------------------------
576include('scripts/Startup.js');
577
578// ================================================================
579// Code to monitor clock conditioner
580// ================================================================
581
582var sub_counter = new Subscription("FTM_CONTROL/COUNTER");
583sub_counter.onchange = function(evt)
584{
585 if (evt.qos>0 && evt.qos!=2 && evt.qos&0x100==0)
586 throw new Error("FTM reports: clock conditioner not locked.");
587}
588
589// ================================================================
590// Code related to monitoring the fad system
591// ================================================================
592
593// This code is here, because scripts/Startup.js needs the
594// same subscriptions... to be revised.
595var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
596var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
597var sub_startrun = new Subscription("FAD_CONTROL/START_RUN");
598sub_startrun.get(5000);
599
600include('scripts/takeRun.js');
601
602// ----------------------------------------------------------------
603// Check that everything we need is availabel to receive commands
604// (FIXME: Should that go to the general CheckState?)
605// ----------------------------------------------------------------
606//console.out("Checking send.");
607checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
608//console.out("Checking send: done");
609
610// ----------------------------------------------------------------
611// Bring feedback into the correct operational state
612// ----------------------------------------------------------------
613//console.out("Feedback init: start.");
614service_feedback.get(5000);
615
616//v8.timeout(3000, function() { var n = dim.state("FEEDBACK").name; if (n=="CurrentCtrlIdle" || n=="CurrentControl") return true; });
617
618// ----------------------------------------------------------------
619// Connect to the DRS_RUNS service
620// ----------------------------------------------------------------
621//console.out("Drs runs init: start.");
622
623var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
624sub_drsruns.get(5000);
625// FIXME: Check if the last DRS calibration was complete?
626
627function getTimeSinceLastDrsCalib()
628{
629 // ----- Time since last DRS Calibration [min] ------
630 var runs = sub_drsruns.get(0);
631 var diff = (new Date()-runs.time)/60000;
632
633 // Warning: 'roi=300' is a number which is not intrisically fixed
634 // but can change depending on the taste of the observers
635 var valid = runs.obj['run'][2]>0 && runs.obj['roi']==300;
636
637 if (valid)
638 dim.log("Last DRS calibration was %.1fmin ago".$(diff));
639 else
640 dim.log("No valid DRS calibration available.");
641
642 return valid ? diff : null;
643}
644
645// ----------------------------------------------------------------
646// Install interrupt handler
647// ----------------------------------------------------------------
648function handleIrq(cmd, args, time, user)
649{
650 console.out("Interrupt received:");
651 console.out(" IRQ: "+cmd);
652 console.out(" Time: "+time);
653 console.out(" User: "+user);
654
655 irq = cmd ? cmd : "stop";
656
657 // This will end a run in progress as if it where correctly stopped
658 if (dim.state("MCP").name=="TakingData")
659 dim.send("MCP/STOP");
660
661 // This will stop a rate scan in progress
662 if (dim.state("RATE_SCAN").name=="InProgress")
663 dim.send("RATE_SCAN/STOP");
664}
665
666dimctrl.setInterruptHandler(handleIrq);
667
668// ----------------------------------------------------------------
669// Make sure we will write files
670// ----------------------------------------------------------------
671dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
672
673// ----------------------------------------------------------------
674// Print some information for the user about the
675// expected first oberservation
676// ----------------------------------------------------------------
677var test = getObservation();
678if (test!=undefined)
679{
680 var n = new Date();
681 if (observations.length>0 && test==-1)
682 dim.log("First observation scheduled for "+observations[0].start.toUTCString()+" [id="+observations[0].id+"]");
683 if (test>=0 && test<observations.length)
684 dim.log("First observation should start immediately ["+observations[test].start.toUTCString()+", id="+observations[test].id+"]");
685 if (observations.length>0 && observations[0].start>n+12*3600*1000)
686 dim.log("No observations scheduled for the next 12 hours!");
687 if (observations.length==0)
688 dim.log("No observations scheduled!");
689}
690
691// ----------------------------------------------------------------
692// Start main loop
693// ----------------------------------------------------------------
694dim.log("Entering main loop.");
695console.out("");
696
697var run = -2; // getObservation never called
698var sub;
699var lastId;
700var nextId;
701var sun = Sun.horizon(-12);
702var system_on; // undefined
703
704function processIrq()
705{
706 if (!irq)
707 return false;
708
709 if (irq.toUpperCase()=="RESCHEDULE")
710 return true;
711
712 if (irq.toUpperCase()=="OFF")
713 {
714 service_feedback.voltageOff();
715 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
716 return true;
717 }
718
719 if (irq.toUpperCase()=="SHUTDOWN")
720 {
721 Shutdown();
722 return true;
723 }
724
725 dim.log("IRQ "+irq+" unhandled... stopping script.");
726 return true;
727}
728
729while (!processIrq())
730{
731 // Check if observation position is still valid
732 // If source position has changed, set run=0
733 var idxObs = getObservation();
734 if (idxObs===undefined)
735 break;
736
737 // we are still waiting for the first observation in the schedule
738 if (idxObs==-1)
739 {
740 // flag that the first observation will be in the future
741 run = -1;
742 v8.sleep(1000);
743 continue;
744 }
745
746 // Check if we have to take action do to sun-rise
747 var was_up = sun.isUp;
748 sun = Sun.horizon(-12);
749 if (!was_up && sun.isUp)
750 {
751 console.out("");
752 dim.log("Sun rise detected.... automatic shutdown initiated!");
753 // FIXME: State check?
754 Shutdown();
755 system_on = false;
756 continue;
757 }
758
759 // Current and next observation target
760 var obs = observations[idxObs];
761 var nextObs = observations[idxObs+1];
762
763 // Check if observation target has changed
764 if (lastId!=obs.id) // !Object.isEqual(obs, nextObs)
765 {
766 dim.log("Starting new observation ["+obs.start.toUTCString()+", id="+obs.id+"]");
767
768 // This is the first source, but we do not come from
769 // a scheduled 'START', so we have to check if the
770 // telescop is operational already
771 sub = 0;
772 if (run<0)
773 {
774 //Startup(); // -> Bias On/Off?, Lid open/closed?
775 //CloseLid();
776 }
777
778 // The first observation had a start-time in the past...
779 // In this particular case start with the last entry
780 // in the list of measurements
781 if (run==-2)
782 sub = obs.length-1;
783
784 run = 0;
785 lastId = obs.id;
786 }
787
788 if (nextObs && nextId!=nextObs.id)
789 {
790 dim.log("Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
791 console.out("");
792 nextId = nextObs.id;
793 }
794
795 if (!nextObs && nextId)
796 {
797 dim.log("No further observation scheduled.");
798 console.out("");
799 nextId = undefined;
800 }
801
802 //if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
803 // throw Error("Last scheduled measurement must be a shutdown.");
804
805 // We are done with all measurement slots for this
806 // observation... wait for next observation
807 if (sub>=obs.length)
808 {
809 v8.sleep(1000);
810 continue;
811 }
812
813 if (system_on===false && obs[sub].task!="STARTUP")
814 {
815 v8.sleep(1000);
816 continue;
817 }
818
819 // Check if sun is still up... only DATA and RATESCAN must be suppressed
820 if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN") && sun.isUp)
821 {
822 var now = new Date();
823 var remaining = (sun.set - now)/60000;
824 console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
825 v8.sleep(60000);
826 continue;
827 }
828
829
830 if (obs[sub].task!="IDLE" && (obs[sub].task!="DATA" && run>0))
831 dim.log("New task ["+obs[sub]+"]");
832
833 // FIXME: Maybe print a warning if Drive is on during day time!
834
835 // It is not ideal that we allow the drive to be on during day time, but
836 // otherwise it is difficult to allow e.g. the STARTUP at the beginning of the night
837 var power_states = sun.isUp || !system_on ? [ "DriveOff", "SystemOn" ] : [ "SystemOn" ];
838 var drive_states = sun.isUp || !system_on ? undefined : [ "Armed", "Tracking", "OnTrack" ];
839
840 // A scheduled task was found, lets check if all servers are
841 // still only and in reasonable states. If this is not the case,
842 // something unexpected must have happend and the script is aborted.
843 //console.out(" Checking states [general]");
844 var table =
845 [
846 [ "TNG_WEATHER" ],
847 [ "MAGIC_WEATHER" ],
848 [ "CHAT" ],
849 [ "SMART_FACT" ],
850 [ "TEMPERATURE" ],
851 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
852 [ "FSC_CONTROL", [ "Connected" ] ],
853 [ "MCP", [ "Idle" ] ],
854 [ "TIME_CHECK", [ "Valid" ] ],
855 [ "PWR_CONTROL", power_states/*[ "SystemOn" ]*/ ],
856 [ "AGILENT_CONTROL_24V", [ "VoltageOn" ] ],
857 [ "AGILENT_CONTROL_50V", [ "VoltageOn" ] ],
858 [ "AGILENT_CONTROL_80V", [ "VoltageOn" ] ],
859 [ "BIAS_CONTROL", [ "VoltageOff", "VoltageOn", "Ramping" ] ],
860 [ "FEEDBACK", [ "Calibrated", "InProgress" ] ],
861 [ "LID_CONTROL", [ "Open", "Closed" ] ],
862 [ "DRIVE_CONTROL", drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
863 [ "FTM_CONTROL", [ "Valid", "TriggerOn" ] ],
864 [ "FAD_CONTROL", [ "Connected", "RunInProgress" ] ],
865 [ "RATE_SCAN", [ "Connected" ] ],
866 [ "RATE_CONTROL", [ "Connected", "GlobalThresholdSet", "InProgress" ] ],
867 [ "GPS_CONTROL", [ "Locked" ] ],
868 ];
869
870
871 if (!checkStates(table))
872 {
873 throw new Error("Something unexpected has happened. One of the servers "+
874 "is in a state in which it should not be. Please,"+
875 "try to find out what happened...");
876 }
877
878 datalogger_subscriptions.check();
879
880 // Check if obs.task is one of the one-time-tasks
881 switch (obs[sub].task)
882 {
883 case "IDLE":
884 v8.sleep(5000);
885 continue;
886
887 case "STARTUP":
888 CloseLid();
889
890 doDrsCalibration("startup"); // will switch the voltage off
891
892 if (irq)
893 break;
894
895 service_feedback.voltageOn();
896 service_feedback.waitForVoltageOn();
897
898 // Before we can switch to 3000 we have to make the right DRS calibration
899 dim.log("Taking single p.e. run.");
900 while (!irq && !takeRun("single-pe", 10000));
901
902 // It is unclear what comes next, so we better switch off the voltage
903 service_feedback.voltageOff();
904
905 system_on = true;
906 dim.log("Task finished [STARTUP]");
907 console.out("");
908 break;
909
910 case "SHUTDOWN":
911 Shutdown();
912 system_on = false;
913
914 // FIXME: Avoid new observations after a shutdown until
915 // the next startup (set run back to -2?)
916 sub++;
917 dim.log("Task finished [SHUTDOWN]");
918 console.out("");
919 //console.out(" Waiting for next startup.", "");
920 continue;
921
922 case "DRSCALIB":
923 doDrsCalibration("drscalib"); // will switch the voltage off
924 dim.log("Task finished [DRSCALIB]");
925 console.out("");
926 break;
927
928 case "SINGLEPE":
929 // The lid must be closes
930 CloseLid();
931
932 // Check if DRS calibration is necessary
933 var diff = getTimeSinceLastDrsCalib();
934 if (diff>30 || diff==null)
935 {
936 doDrsCalibration("singlepe"); // will turn voltage off
937 if (irq)
938 break;
939 }
940
941 // The voltage must be on
942 service_feedback.voltageOn();
943 service_feedback.waitForVoltageOn();
944
945 // Before we can switch to 3000 we have to make the right DRS calibration
946 dim.log("Taking single p.e. run.");
947 while (!irq && !takeRun("single-pe", 10000));
948
949 // It is unclear what comes next, so we better switch off the voltage
950 service_feedback.voltageOff();
951 dim.log("Task finished [SINGLE-PE]");
952 console.out("");
953 break;
954
955 case "OVTEST":
956 var locked = dim.state("DRIVE_CONTROL").name=="Locked";
957 if (!locked)
958 dim.send("DRIVE_CONTROL/PARK");
959
960 dim.send("FEEDBACK/STOP");
961
962 // The lid must be closed
963 CloseLid();
964
965 if (!locked)
966 {
967 //console.out("Waiting for telescope to park. This may take a while.");
968 dim.wait("DRIVE_CONTROL", "Locked", 3000);
969 dim.send("DRIVE_CONTROL/UNLOCK");
970 }
971
972 // Check if DRS calibration is necessary
973 var diff = getTimeSinceLastDrsCalib();
974 if (diff>30 || diff==null)
975 {
976 doDrsCalibration("ovtest"); // will turn voltage off
977 if (irq)
978 break;
979 }
980
981 // The voltage must be on
982 service_feedback.voltageOn(0.4);
983 service_feedback.waitForVoltageOn();
984
985 dim.log("Taking single p.e. run (0.4V)");
986 while (!irq && !takeRun("single-pe", 10000));
987
988 for (var i=5; i<18 && !irq; i++)
989 {
990 dim.send("FEEDBACK/STOP");
991 dim.wait("FEEDBACK", "Calibrated", 3000);
992 dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
993 dim.send("FEEDBACK/START", i*0.1);
994 dim.wait("FEEDBACK", "InProgress", 45000);
995 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
996 service_feedback.waitForVoltageOn();
997 dim.log("Taking single p.e. run ("+(i*0.1)+"V)");
998 while (!irq && !takeRun("single-pe", 10000));
999 }
1000
1001 // It is unclear what comes next, so we better switch off the voltage
1002 service_feedback.voltageOff();
1003 dim.log("Task finished [OVTEST]");
1004 console.out("");
1005 break;
1006
1007 case "RATESCAN":
1008 var tm1 = new Date();
1009
1010 // This is a workaround to make sure that we really catch
1011 // the new OnTrack state later and not the old one
1012 dim.send("DRIVE_CONTROL/STOP");
1013 dim.wait("DRIVE_CONTROL", "Armed", 15000);
1014
1015 // The lid must be open
1016 OpenLid();
1017
1018 // Switch the voltage to a reduced level (Ubd)
1019 service_feedback.voltageOn(0);
1020
1021 if (obs.source != undefined)
1022 {
1023 dim.log("Pointing telescope to '"+obs[cub].source+"'.");
1024 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
1025 }
1026 else
1027 {
1028 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
1029 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
1030 }
1031
1032 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1033
1034 // Now tracking stable, switch voltage to nominal level and wait
1035 // for stability.
1036 service_feedback.voltageOn();
1037 service_feedback.waitForVoltageOn();
1038
1039 var tm2 = new Date();
1040
1041 dim.log("Starting ratescan.");
1042
1043 // Start rate scan
1044 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10);
1045
1046 // Lets wait if the ratescan really starts... this might take a few
1047 // seconds because RATE_SCAN configures the ftm and is waiting for
1048 // it to be configured.
1049 dim.wait("RATE_SCAN", "InProgress", 10000);
1050 dim.wait("RATE_SCAN", "Connected", 2700000);
1051
1052 // this line is actually some kind of hack.
1053 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
1054 // So I decided to put this line here as a kind of patchwork....
1055 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
1056
1057 dim.log("Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
1058 dim.log("Task finished [RATESCAN]");
1059 console.out("");
1060 break; // case "RATESCAN"
1061
1062 case "DATA":
1063
1064 // ========================== case "DATA" ============================
1065 /*
1066 if (Sun.horizon("FACT").isUp)
1067 {
1068 console.out(" SHUTDOWN","");
1069 Shutdown();
1070 console.out(" Exit forced due to broken schedule", "");
1071 exit();
1072 }
1073 */
1074 // Calculate remaining time for this observation in minutes
1075 var remaining = nextObs==undefined ? 0 : (nextObs.start-new Date())/60000;
1076
1077 // ------------------------------------------------------------
1078
1079 dim.log("Run count "+run+" [remaining "+parseInt(remaining)+"min]");
1080
1081 // ----- Time since last DRS Calibration [min] ------
1082 var diff = getTimeSinceLastDrsCalib();
1083
1084 // Changine pointing position and take calibration...
1085 // ...every four runs (every ~20min)
1086 // ...if at least ten minutes of observation time are left
1087 // ...if this is the first run on the source
1088 var point = (run%4==0 && remaining>10) || run==0;
1089
1090 // Take DRS Calib...
1091 // ...every four runs (every ~20min)
1092 // ...at last every two hours
1093 // ...when DRS temperature has changed by more than 2deg (?)
1094 // ...when more than 15min of observation are left
1095 // ...no drs calibration was done yet
1096 var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null;
1097
1098 if (point)
1099 {
1100 // Switch the voltage to a reduced voltage level
1101 service_feedback.voltageOn(0);
1102
1103 // Change wobble position every four runs,
1104 // start with alternating wobble positions each day
1105 var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
1106
1107 //console.out(" Move telescope to '"+source+"' "+offset+" "+wobble);
1108 dim.log("Pointing telescope to '"+obs[sub].source+"' [wobble="+wobble+"]");
1109
1110 // This is a workaround to make sure that we really catch
1111 // the new OnTrack state later and not the old one
1112 dim.send("DRIVE_CONTROL/STOP");
1113 dim.wait("DRIVE_CONTROL", "Armed", 15000);
1114
1115 dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
1116
1117 // Do we have to check if the telescope is really moving?
1118 // We can cross-check the SOURCE service later
1119 }
1120
1121 if (drscal)
1122 {
1123 doDrsCalibration("data"); // will turn voltage off
1124
1125 // Now we switch on the voltage and a significant amount of
1126 // time has been passed, so do the check again.
1127 sun = Sun.horizon(-12);
1128 if (!was_up && sun.isUp)
1129 {
1130 dim.log("Sun rise detected....");
1131 continue;
1132 }
1133 }
1134
1135 if (irq)
1136 break;
1137
1138 OpenLid();
1139
1140 // This is now th right time to wait for th drive to be stable
1141 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
1142
1143 // Now we are 'OnTrack', so we can ramp to nominal voltage
1144 // and wait for the feedback to get stable
1145 service_feedback.voltageOn();
1146 service_feedback.waitForVoltageOn();
1147
1148 // If pointing had changed, do calibration
1149 if (point)
1150 {
1151 dim.log("Starting calibration.");
1152
1153 // Calibration (2% of 20')
1154 while (!irq)
1155 {
1156 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s
1157 continue;
1158 if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s
1159 continue;
1160 break;
1161 }
1162 }
1163
1164 //console.out(" Taking data: start [5min]");
1165
1166 // FIXME: What do we do if during calibration something has happened
1167 // e.g. drive went to ERROR? Maybe we have to check all states again?
1168
1169 var twilight = Sun.horizon(-16).isUp;
1170
1171 if (twilight)
1172 {
1173 for (var i=0; i<5 && !irq; i++)
1174 takeRun("data", -1, 60); // Take data (1min)
1175 }
1176 else
1177 {
1178 var len = 300;
1179 while (len>15)
1180 {
1181 var time = new Date();
1182 if (takeRun("data", -1, len)) // Take data (5min)
1183 break;
1184
1185 len -= parseInt((new Date()-time)/1000);
1186 }
1187 }
1188
1189 //console.out(" Taking data: done");
1190 run++;
1191
1192 continue; // case "DATA"
1193 }
1194
1195 if (nextObs!=undefined && sub==obs.length-1)
1196 dim.log("Next observation will start at "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
1197
1198 sub++;
1199}
1200
1201sub_drsruns.close();
1202
1203dim.log("Left main loop [irq="+irq+"]");
1204
1205// ================================================================
1206// Comments and ToDo goes here
1207// ================================================================
1208
1209// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
1210// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
Note: See TracBrowser for help on using the repository browser.