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

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