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

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