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

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