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

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