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

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