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 7 dim.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 // 15 if (!$['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' 25 include('scripts/Observation_class.js'); 26 include('scripts/getSchedule.js'); 27 28 var observations = [ ]; 29 30 // Get the observation scheduled for 'now' from the table and 31 // return its index 32 function 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 /* 53 function 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 70 function 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 92 var irq; 93 94 function 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 (where!="data") 112 { 113 if (irq || !takeRun("drs-pedestal", 1000)) // 40 / 20s (50Hz) 114 continue; 115 } 116 117 break; 118 } 119 120 if (where!="data") 121 { 122 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 123 124 while (!irq && !takeRun("drs-pedestal", 1000)); // 40 / 20s (50Hz) 125 while (!irq && !takeRun("drs-time", 1000)); // 40 / 20s (50Hz) 126 } 127 128 while (!irq) 129 { 130 dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE"); 131 if (takeRun("pedestal", 1000)) // 40 / 10s (80Hz) 132 break; 133 } 134 135 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 136 137 while (!irq && !takeRun("pedestal", 1000)); // 40 / 10s (80Hz) 138 // ----------- 139 // 4'40 / 2'00 140 141 if (irq) 142 dim.log("DRS calibration interrupted [%.1fs]".$((new Date()-tm)/1000)); 143 else 144 dim.log("DRS calibration done [%.1fs]".$((new Date()-tm)/1000)); 145 } 146 147 // ================================================================ 148 // Code related to the lid 149 // ================================================================ 150 151 function OpenLid() 152 { 153 /* 154 while (Sun.horizon(-13).isUp) 155 { 156 var now = new Date(); 157 var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000; 158 console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset)); 159 v8.sleep(60000); 160 }*/ 161 162 var isClosed = dim.state("LID_CONTROL").name=="Closed"; 163 var isInconsistent = dim.state("LID_CONTROL").name=="Inconsistent"; 164 165 var tm = new Date(); 166 167 // Wait for lid to be open 168 if (isClosed || isInconsistent) 169 { 170 dim.log("Opening lid"); 171 dim.send("LID_CONTROL/OPEN"); 172 173 dim.log("Turning off IR camera LEDs..."); 174 175 var cam = new Curl("fact@cam/cgi-bin/user/Config.cgi"); 176 cam.data.push("action=set"); 177 cam.data.push("Camera.System.Title=Camera1"); 178 cam.data.push("Camera.General.IRControl.Value=2"); 179 cam.data.push("Camera.System.Display=ALL"); 180 cam.data.push("Camera.Environment=OUTDOOR"); 181 var ret = cam.send(); 182 dim.log("Camera response: "+ret.data.replace("\n","/")+" ["+ret.rc+"]"); 183 } 184 dim.wait("LID_CONTROL", "Open", 30000); 185 186 if (isClosed || isInconsistent) 187 dim.log("Lid open [%.1fs]".$((new Date()-tm)/1000)); 188 } 189 190 function CloseLid() 191 { 192 var isOpen = dim.state("LID_CONTROL").name=="Open"; 193 194 var tm = new Date(); 195 196 // Wait for lid to be open 197 if (isOpen) 198 { 199 if (dim.state("FTM_CONTROL").name=="TriggerOn") 200 { 201 dim.send("FTM_CONTROL/STOP_TRIGGER"); 202 dim.wait("FTM_CONTROL", "Valid", 3000); 203 } 204 205 dim.log("Closing lid."); 206 dim.send("LID_CONTROL/CLOSE"); 207 } 208 v8.timeout(30000, function() { if (dim.state("LID_CONTROL").name=="Closed" || dim.state("LID_CONTROL").name=="Inconsistent") return true; }); 209 //dim.wait("LID_CONTROL", "Closed", 30000); 210 //dim.wait("LID_CONTROL", "Inconsistent", 30000); 211 212 if (isOpen) 213 dim.log("Lid closed [%.1fs]".$((new Date()-tm)/1000)); 214 } 215 216 // ================================================================ 217 // Interrupt data taking in case of high currents 218 // ================================================================ 219 dim.onchange['FEEDBACK'] = function(state) 220 { 221 if ((state.name=="Critical" || state.name=="OnStandby") && 222 (this.prev!="Critical" && this.prev!="OnStandby")) 223 { 224 console.out("Feedback state changed from "+this.prev+" to "+state.name+" [Main.js]"); 225 irq = "RESCHEDULE"; 226 } 227 this.prev=state.name; 228 } 229 230 // ================================================================ 231 // Code related to switching bias voltage on and off 232 // ================================================================ 233 234 var service_feedback = new Subscription("FEEDBACK/CALIBRATED_CURRENTS"); 235 236 service_feedback.onchange = function(evt) 237 { 238 if (!evt.data) 239 return; 240 241 if (this.ok==undefined) 242 return; 243 244 var Unom = evt.obj['U_nom']; 245 var Uov = evt.obj['U_ov']; 246 if (!Uov) 247 return; 248 249 var cnt = 0; 250 var avg = 0; 251 for (var i=0; i<320; i++) 252 { 253 // This is a fix for the channel with a shortcut 254 if (i==272) 255 continue; 256 257 var dU = Uov[i]-Unom; 258 259 // 0.022 corresponds to 1 DAC count (90V/4096) 260 if (Math.abs(dU)>0.033) 261 cnt++; 262 263 avg += dU; 264 } 265 avg /= 320; 266 267 this.ok = cnt<3;// || (this.last!=undefined && Math.abs(this.last-avg)<0.002); 268 269 console.out(" DeltaUov=%.3f (%.3f) [N(>0.033V)=%d]".$(avg, avg-this.last, cnt)); 270 271 this.last = avg; 272 } 273 274 service_feedback.voltageOff = function() 275 { 276 var state = dim.state("BIAS_CONTROL").name; 277 278 if (state=="Disconnected") 279 { 280 console.out(" Voltage off: bias crate disconnected!"); 281 return; 282 } 283 284 // check of feedback has to be switched on 285 var isOn = state=="VoltageOn" || state=="Ramping"; 286 if (isOn) 287 { 288 dim.log("Switching voltage off."); 289 290 if (dim.state("FTM_CONTROL").name=="TriggerOn") 291 { 292 dim.send("FTM_CONTROL/STOP_TRIGGER"); 293 dim.wait("FTM_CONTROL", "Valid", 3000); 294 } 295 296 // Supress the possibility that the bias control is 297 // ramping and will reject the command to switch the 298 // voltage off 299 //dim.send("FEEDBACK/STOP"); 300 //dim.wait("FEEDBACK", "Calibrated", 3000); 301 302 // Make sure we are not in Ramping anymore 303 //dim.wait("BIAS_CONTROL", "VoltageOn", 3000); 304 305 // Switch voltage off 306 dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE"); 307 } 308 309 dim.wait("BIAS_CONTROL", "VoltageOff", 60000); // FIXME: 30000? 310 dim.wait("FEEDBACK", "Calibrated", 3000); 311 312 // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled 313 // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000); 314 315 if (isOn) 316 dim.log("Voltage off."); 317 } 318 319 // DN: The name of the method voltageOn() in the context of the method 320 // voltageOff() is a little bit misleading, since when voltageOff() returns 321 // the caller can be sure the voltage is off, but when voltageOn() return 322 // this is not the case, in the sense, that the caller can now take data. 323 // instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards 324 // in order to safely take good-quality data. 325 // This could lead to nasty bugs in the sense, that the second call might 326 // be forgotten by somebody 327 // 328 // so I suggest to rename voltageOn() --> prepareVoltageOn() 329 // waitForVoltageOn() stays as it is 330 // and one creates a third method called:voltageOn() like this 331 /* service_feedback.voltageOn = function() 332 * { 333 * this.prepareVoltageOn(); 334 * this.waitForVoltageOn(); 335 * } 336 * 337 * */ 338 // For convenience. 339 340 service_feedback.voltageOn = function(ov) 341 { 342 if (isNaN(ov)) 343 ov = 1.1; 344 345 if (this.ov!=ov && dim.state("FEEDBACK").name=="InProgress") // FIXME: Warning, OnStandby, Critical if (ov<this.ov) 346 { 347 dim.log("Stoping feedback."); 348 if (dim.state("FTM_CONTROL").name=="TriggerOn") 349 { 350 dim.send("FTM_CONTROL/STOP_TRIGGER"); 351 dim.wait("FTM_CONTROL", "Valid", 3000); 352 } 353 354 dim.send("FEEDBACK/STOP"); 355 dim.wait("FEEDBACK", "Calibrated", 3000); 356 357 // Make sure we are not in Ramping anymore 358 dim.wait("BIAS_CONTROL", "VoltageOn", 3000); 359 } 360 361 var isOff = dim.state("FEEDBACK").name=="Calibrated"; 362 if (isOff) 363 { 364 dim.log("Switching voltage to Uov="+ov+"V."); 365 366 dim.send("FEEDBACK/START", ov); 367 368 // FIXME: We could miss "InProgress" if it immediately changes to "Warning" 369 // Maybe a dim.timeout state>8 ? 370 dim.wait("FEEDBACK", "InProgress", 45000); 371 372 this.ov = ov; 373 } 374 375 // Wait until voltage on 376 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000? 377 } 378 379 service_feedback.waitForVoltageOn = function() 380 { 381 // Avoid output if condition is already fulfilled 382 dim.log("Waiting for voltage to be stable."); 383 384 function func() 385 { 386 if (irq || this.ok==true) 387 return true; 388 } 389 390 var now = new Date(); 391 392 this.last = undefined; 393 this.ok = false; 394 v8.timeout(4*60000, func, this); // FIMXE: Remove 4! 395 this.ok = undefined; 396 397 if (irq) 398 dim.log("Waiting for stable voltage interrupted."); 399 else 400 dim.log("Voltage stable within limits"); 401 } 402 403 // ================================================================ 404 // Function to shutdown the system 405 // ================================================================ 406 407 function Shutdown(type) 408 { 409 if (!type) 410 type = "default"; 411 412 dim.log("Starting shutdown ["+type+"]."); 413 414 var now1 = new Date(); 415 416 var bias = dim.state("BIAS_CONTROL").name; 417 if (bias=="VoltageOn" || bias=="Ramping") 418 service_feedback.voltageOn(0); 419 420 CloseLid(); 421 422 var now2 = new Date(); 423 424 dim.send("DRIVE_CONTROL/PARK"); 425 426 console.out("","Waiting for telescope to park. This may take a while."); 427 428 // FIXME: This might not work is the drive is already close to park position 429 //dim.wait("DRIVE_CONTROL", "Parking", 3000); 430 431 /* 432 // Check if DRS calibration is necessary 433 var diff = getTimeSinceLastDrsCalib(); 434 if (diff>30 || diff==null) 435 { 436 doDrsCalibration("singlepe"); // will turn voltage off 437 if (irq) 438 break; 439 }*/ 440 441 //take single pe run if required 442 if (type=="singlepe") 443 { 444 dim.log("Taking single-pe run."); 445 446 // The voltage must be on 447 service_feedback.voltageOn(); 448 service_feedback.waitForVoltageOn(); 449 450 // Before we can switch to 3000 we have to make the right DRS calibration 451 dim.log("Taking single p.e. run."); 452 while (!irq && !takeRun("single-pe", 10000)); 453 454 /* 455 Maybe we need to send a trigger... but data runs contain pedestal triggers... so it should work in any case... 456 var customRun = function() 457 { 458 v8.sleep(500);//wait that configuration is set 459 dim.wait("FTM_CONTROL", "TriggerOn", 15000); 460 dim.send("FAD_CONTROL/SEND_SINGLE_TRIGGER"); 461 dim.send("RATE_CONTROL/STOP"); 462 dim.send("FTM_CONTROL/STOP_TRIGGER"); 463 dim.wait("FTM_CONTROL", "Valid", 3000); 464 dim.send("FTM_CONTROL/ENABLE_TRIGGER", true); 465 dim.send("FTM_CONTROL/SET_TIME_MARKER_DELAY", 123); 466 dim.send("FTM_CONTROL/SET_THRESHOLD", -1, obs[sub].threshold); 467 v8.sleep(500);//wait that configuration is set 468 dim.send("FTM_CONTROL/START_TRIGGER"); 469 dim.wait("FTM_CONTROL", "TriggerOn", 15000); 470 }*/ 471 } 472 473 //wait until drive is in locked (after it reached park position) 474 dim.wait("DRIVE_CONTROL", "Locked", 150000); 475 476 //unlock drive if task was sleep 477 if (type=="sleep") 478 dim.send("DRIVE_CONTROL/UNLOCK"); 479 480 481 // It is unclear what comes next, so we better switch off the voltage 482 service_feedback.voltageOff(); 483 484 dim.log("Finishing shutdown."); 485 486 var now3 = new Date(); 487 488 dim.send("FTM_CONTROL/STOP_TRIGGER"); 489 dim.wait("FTM_CONTROL", "Valid", 3000); 490 491 if (bias!="Disconnected") 492 dim.wait("FEEDBACK", "Calibrated", 3000); 493 494 if (type!="sleep") 495 { 496 dim.send("BIAS_CONTROL/DISCONNECT"); 497 498 var pwrctrl_state = dim.state("PWR_CONTROL").name; 499 if (pwrctrl_state=="SystemOn" || 500 pwrctrl_state=="BiasOff" || 501 pwrctrl_state=="DriveOn") 502 dim.send("PWR_CONTROL/TOGGLE_DRIVE"); 503 504 dim.wait("BIAS_CONTROL", "Disconnected", 3000); 505 dim.wait("PWR_CONTROL", "DriveOff", 6000); 506 } 507 508 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION"); 509 sub.get(5000); // FIXME: Proper error message in case of failure 510 511 var report = sub.get(); 512 513 console.out(""); 514 console.out("Shutdown procedure ["+type+"] seems to be finished..."); 515 console.out(" "+new Date().toUTCString()); 516 console.out(" Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az'])); 517 console.out(" Please check on the web cam that the park position was reached"); 518 console.out(" and the telescope is not moving anymore."); 519 console.out(" Please check visually that the lid is really closed and"); 520 console.out(" that the biasctrl really switched the voltage off.", ""); 521 console.out(" DRIVE_CONTROL: "+dim.state("DRIVE_CONTROL").name); 522 console.out(" FEEDBACK: "+dim.state("FEEDBACK").name); 523 console.out(" FTM_CONTROL: "+dim.state("FTM_CONTROL").name); 524 console.out(" BIAS_CONTROL: "+dim.state("BIAS_CONTROL").name); 525 console.out(" PWR_CONTROL: "+dim.state("PWR_CONTROL").name); 526 console.out(""); 527 dim.log("Shutdown: end ["+(now2-now1)/1000+"s, "+(now3-now2)/1000+"s, "+(new Date()-now3)/1000+"s]"); 528 console.out(""); 529 530 sub.close(); 531 } 532 533 534 // ================================================================ 535 // Function to set the system to sleep-mode 536 // ================================================================ 537 // FIXME: do not repeat code from shutdown-function 538 /* 539 function GoToSleep() 540 { 541 CloseLid(); 542 543 var isArmed = dim.state("DRIVE_CONTROL").name=="Armed"; 544 if (!isArmed) 545 { 546 dim.log("Drive not ready to move. -> send STOP"); 547 dim.send("DRIVE_CONTROL/STOP"); 548 dim.wait("DRIVE_CONTROL", "Armed", 5000); 549 } 550 551 dim.send("DRIVE_CONTROL/MOVE_TO 101 0");//park position 552 var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION"); 553 sub.get(5000); // FIXME: Proper error message in case of failure 554 555 function func() 556 { 557 var report = sub.get(); 558 559 var zd = report.obj['Zd']; 560 var az = report.obj['Az']; 561 562 if (zd>100 && Math.abs(az)<1) 563 return true; 564 565 return undefined; 566 } 567 568 try { v8.timeout(150000, func); } 569 catch (e) 570 { 571 var p = sub.get(); 572 dim.log('Park position not reached? Telescope at Zd='+p.obj['Zd']+' Az='+p.obj['Az']); 573 } 574 var p2 = sub.get(); 575 dim.log('Telescope at Zd=%.1fdeg Az=%.1fdeg'.$(p2.obj['Zd'], p2.obj['Az'])); 576 sub.close(); 577 } 578 */ 579 580 // ================================================================ 581 // Check datalogger subscriptions 582 // ================================================================ 583 584 var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS"); 585 datalogger_subscriptions.get(3000, false); 586 587 datalogger_subscriptions.check = function() 588 { 589 var obj = this.get(); 590 if (!obj.data) 591 throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available."); 592 593 var expected = 594 [ 595 "AGILENT_CONTROL_24V/DATA", 596 "AGILENT_CONTROL_50V/DATA", 597 "AGILENT_CONTROL_80V/DATA", 598 "BIAS_CONTROL/CURRENT", 599 "BIAS_CONTROL/DAC", 600 "BIAS_CONTROL/NOMINAL", 601 "BIAS_CONTROL/VOLTAGE", 602 "DRIVE_CONTROL/POINTING_POSITION", 603 "DRIVE_CONTROL/SOURCE_POSITION", 604 "DRIVE_CONTROL/STATUS", 605 "DRIVE_CONTROL/TRACKING_POSITION", 606 "FAD_CONTROL/CONNECTIONS", 607 "FAD_CONTROL/DAC", 608 "FAD_CONTROL/DNA", 609 "FAD_CONTROL/DRS_RUNS", 610 "FAD_CONTROL/EVENTS", 611 "FAD_CONTROL/FEEDBACK_DATA", 612 "FAD_CONTROL/FILE_FORMAT", 613 "FAD_CONTROL/FIRMWARE_VERSION", 614 "FAD_CONTROL/INCOMPLETE", 615 "FAD_CONTROL/PRESCALER", 616 "FAD_CONTROL/REFERENCE_CLOCK", 617 "FAD_CONTROL/REGION_OF_INTEREST", 618 "FAD_CONTROL/RUNS", 619 "FAD_CONTROL/RUN_NUMBER", 620 "FAD_CONTROL/START_RUN", 621 "FAD_CONTROL/STATISTICS1", 622 "FAD_CONTROL/STATS", 623 "FAD_CONTROL/STATUS", 624 "FAD_CONTROL/TEMPERATURE", 625 "FEEDBACK/CALIBRATED_CURRENTS", 626 "FEEDBACK/CALIBRATION", 627 "FEEDBACK/CALIBRATION_R8", 628 "FEEDBACK/CALIBRATION_STEPS", 629 /* "FEEDBACK/REFERENCE",*/ 630 "FSC_CONTROL/CURRENT", 631 "FSC_CONTROL/HUMIDITY", 632 "FSC_CONTROL/TEMPERATURE", 633 "FSC_CONTROL/VOLTAGE", 634 "FTM_CONTROL/COUNTER", 635 "FTM_CONTROL/DYNAMIC_DATA", 636 "FTM_CONTROL/ERROR", 637 "FTM_CONTROL/FTU_LIST", 638 "FTM_CONTROL/PASSPORT", 639 "FTM_CONTROL/STATIC_DATA", 640 "FTM_CONTROL/TRIGGER_RATES", 641 "GPS_CONTROL/NEMA", 642 "SQM_CONTROL/DATA", 643 "LID_CONTROL/DATA", 644 "MAGIC_LIDAR/DATA", 645 "MAGIC_WEATHER/DATA", 646 "MCP/CONFIGURATION", 647 "PWR_CONTROL/DATA", 648 "RATE_CONTROL/THRESHOLD", 649 "RATE_SCAN/DATA", 650 "RATE_SCAN/PROCESS_DATA", 651 "TEMPERATURE/DATA", 652 "TIME_CHECK/OFFSET", 653 "TNG_WEATHER/DATA", 654 "TNG_WEATHER/DUST", 655 "PFMINI_CONTROL/DATA", 656 ]; 657 658 function map(entry) 659 { 660 if (entry.length==0) 661 return undefined; 662 663 var rc = entry.split(','); 664 if (rc.length!=2) 665 throw new Error("Subscription list entry '"+entry+"' has wrong number of elements."); 666 return rc; 667 } 668 669 var list = obj.data.split('\n').map(map); 670 function check(name) 671 { 672 if (list.every(function(el){return el==undefined || el[0]!=name;})) 673 throw new Error("Subscription to '"+name+"' not available."); 674 } 675 676 expected.forEach(check); 677 } 678 679 680 681 // ================================================================ 682 // Crosscheck all states 683 // ================================================================ 684 685 // ---------------------------------------------------------------- 686 // Do a standard startup to bring the system in into a well 687 // defined state 688 // ---------------------------------------------------------------- 689 include('scripts/Startup.js'); 690 691 // ================================================================ 692 // Code to monitor clock conditioner 693 // ================================================================ 694 695 var sub_counter = new Subscription("FTM_CONTROL/COUNTER"); 696 sub_counter.onchange = function(evt) 697 { 698 if (evt.qos>0 && evt.qos!=2 && evt.qos&0x100==0) 699 throw new Error("FTM reports: clock conditioner not locked."); 700 } 701 702 // ================================================================ 703 // Code related to monitoring the fad system 704 // ================================================================ 705 706 // This code is here, because scripts/Startup.js needs the 707 // same subscriptions... to be revised. 708 var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE"); 709 var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS"); 710 var sub_startrun = new Subscription("FAD_CONTROL/START_RUN"); 711 sub_startrun.get(5000); 712 713 include('scripts/takeRun.js'); 714 715 // ---------------------------------------------------------------- 716 // Check that everything we need is availabel to receive commands 717 // (FIXME: Should that go to the general CheckState?) 718 // ---------------------------------------------------------------- 719 //console.out("Checking send."); 720 checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]); 721 //console.out("Checking send: done"); 722 723 // ---------------------------------------------------------------- 724 // Bring feedback into the correct operational state 725 // ---------------------------------------------------------------- 726 //console.out("Feedback init: start."); 727 service_feedback.get(5000); 728 729 // ---------------------------------------------------------------- 730 // Connect to the DRS_RUNS service 731 // ---------------------------------------------------------------- 732 //console.out("Drs runs init: start."); 733 734 var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS"); 735 sub_drsruns.get(5000); 736 // FIXME: Check if the last DRS calibration was complete? 737 738 function getTimeSinceLastDrsCalib() 739 { 740 // ----- Time since last DRS Calibration [min] ------ 741 var runs = sub_drsruns.get(0); 742 var diff = (new Date()-runs.time)/60000; 743 744 // Warning: 'roi=300' is a number which is not intrisically fixed 745 // but can change depending on the taste of the observers 746 var valid = runs.obj['run'][2]>0 && runs.obj['roi']==300; 747 748 if (valid) 749 dim.log("Last DRS calibration was %.1fmin ago".$(diff)); 750 else 751 dim.log("No valid DRS calibration available."); 752 753 return valid ? diff : null; 754 } 755 756 // ---------------------------------------------------------------- 757 // Install interrupt handler 758 // ---------------------------------------------------------------- 759 function handleIrq(cmd, args, time, user) 760 { 761 console.out("Interrupt received:"); 762 console.out(" IRQ: "+cmd); 763 console.out(" Time: "+time); 764 console.out(" User: "+user); 765 766 irq = cmd ? cmd : "stop"; 767 768 // This will end a run in progress as if it where correctly stopped 769 if (dim.state("MCP").name=="TakingData") 770 dim.send("MCP/STOP"); 771 772 // This will stop a rate scan in progress 773 if (dim.state("RATE_SCAN").name=="InProgress") 774 dim.send("RATE_SCAN/STOP"); 775 } 776 777 dimctrl.setInterruptHandler(handleIrq); 778 779 // ---------------------------------------------------------------- 780 // Make sure we will write files 781 // ---------------------------------------------------------------- 782 dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 783 784 // ---------------------------------------------------------------- 785 // Print some information for the user about the 786 // expected first oberservation 787 // ---------------------------------------------------------------- 788 var test = getObservation(); 789 if (test!=undefined) 790 { 791 var n = new Date(); 792 if (observations.length>0 && test==-1) 793 dim.log("First observation scheduled for "+observations[0].start.toUTCString()+" [id="+observations[0].id+"]"); 794 if (test>=0 && test<observations.length) 795 dim.log("First observation should start immediately ["+observations[test].start.toUTCString()+", id="+observations[test].id+"]"); 796 if (observations.length>0 && observations[0].start>n+12*3600*1000) 797 dim.log("No observations scheduled for the next 12 hours!"); 798 if (observations.length==0) 799 dim.log("No observations scheduled!"); 800 } 801 802 // ---------------------------------------------------------------- 803 // Start main loop 804 // ---------------------------------------------------------------- 805 dim.log("Entering main loop."); 806 console.out(""); 807 808 var run = -2; // getObservation never called 809 var sub; 810 var lastId; 811 var nextId; 812 var sun = Sun.horizon(-12); 813 var system_on; // undefined 814 815 function processIrq() 816 { 817 if (!irq) 818 return false; 819 820 if (irq.toUpperCase()=="RESCHEDULE") 821 { 822 irq = undefined; 823 return false; 824 } 825 826 if (irq.toUpperCase()=="OFF") 827 { 828 service_feedback.voltageOff(); 829 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES"); 830 return true; 831 } 832 833 /* 834 if (irq.toUpperCase()=="STOP") 835 { 836 dim.send("FAD_CONTROL/CLOSE_OPEN_FILES"); 837 dim.send("MCP/STOP"); 838 return true; 839 }*/ 840 841 if (irq.toUpperCase()=="SHUTDOWN") 842 { 843 Shutdown(); 844 return true; 845 } 846 847 dim.log("IRQ "+irq+" unhandled... stopping script."); 848 return true; 849 } 850 851 while (!processIrq()) 852 { 853 // Check if observation position is still valid 854 // If source position has changed, set run=0 855 var idxObs = getObservation(); 856 if (idxObs===undefined) 857 break; 858 859 // we are still waiting for the first observation in the schedule 860 if (idxObs==-1) 861 { 862 // flag that the first observation will be in the future 863 run = -1; 864 v8.sleep(1000); 865 continue; 866 } 867 868 // Check if we have to take action do to sun-rise 869 var was_up = sun.isUp; 870 sun = Sun.horizon(-12); 871 if (!was_up && sun.isUp) 872 { 873 console.out(""); 874 dim.log("Sun rise detected.... automatic shutdown initiated!"); 875 // FIXME: State check? 876 Shutdown(); 877 system_on = false; 878 continue; 879 } 880 881 // Current and next observation target 882 var obs = observations[idxObs]; 883 var nextObs = observations[idxObs+1]; 884 885 // Check if observation target has changed 886 if (lastId!=obs.id) // !Object.isEqual(obs, nextObs) 887 { 888 dim.log("Starting new observation ["+obs.start.toUTCString()+", id="+obs.id+"]"); 889 890 // This is the first source, but we do not come from 891 // a scheduled 'START', so we have to check if the 892 // telescop is operational already 893 sub = 0; 894 if (run<0) 895 { 896 //Startup(); // -> Bias On/Off?, Lid open/closed? 897 //CloseLid(); 898 } 899 900 // The first observation had a start-time in the past... 901 // In this particular case start with the last entry 902 // in the list of measurements 903 if (run==-2) 904 sub = obs.length-1; 905 906 run = 0; 907 lastId = obs.id; 908 } 909 910 //dim.log("DEBUG: Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]"); 911 if (nextObs && nextId!=nextObs.id) 912 { 913 dim.log("Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]"); 914 console.out(""); 915 nextId = nextObs.id; 916 } 917 918 if (!nextObs && nextId) 919 { 920 dim.log("No further observation scheduled."); 921 console.out(""); 922 nextId = undefined; 923 } 924 925 //if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN") 926 // throw Error("Last scheduled measurement must be a shutdown."); 927 928 // We are done with all measurement slots for this 929 // observation... wait for next observation 930 if (sub>=obs.length) 931 { 932 v8.sleep(1000); 933 continue; 934 } 935 936 if (system_on===false && obs[sub].task!="STARTUP") 937 { 938 v8.sleep(1000); 939 continue; 940 } 941 942 // Check if sun is still up... only DATA and */ 943 if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN" || obs[sub].task=="RATESCAN2" ) && sun.isUp) 944 { 945 var now = new Date(); 946 var remaining = (sun.set - now)/60000; 947 console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining)); 948 v8.sleep(60000); 949 continue; 950 } 951 952 953 if (obs[sub].task!="IDLE" && (obs[sub].task!="DATA" && run>0)) 954 dim.log("New task ["+obs[sub]+"]"); 955 956 // FIXME: Maybe print a warning if Drive is on during day time! 957 958 // It is not ideal that we allow the drive to be on during day time, but 959 // otherwise it is difficult to allow e.g. the STARTUP at the beginning of the night 960 var power_states = sun.isUp || !system_on ? [ "DriveOff", "SystemOn" ] : [ "SystemOn" ]; 961 var drive_states = sun.isUp || !system_on ? undefined : [ "Initialized", "Tracking", "OnTrack" ]; 962 963 // A scheduled task was found, lets check if all servers are 964 // still only and in reasonable states. If this is not the case, 965 // something unexpected must have happend and the script is aborted. 966 //console.out(" Checking states [general]"); 967 var table = 968 [ 969 [ "TNG_WEATHER" ], 970 [ "MAGIC_WEATHER" ], 971 [ "CHAT" ], 972 [ "SMART_FACT" ], 973 [ "TEMPERATURE" ], 974 [ "DATA_LOGGER", [ "NightlyFileOpen", "WaitForRun", "Logging" ] ], 975 [ "FSC_CONTROL", [ "Connected" ] ], 976 [ "MCP", [ "Idle" ] ], 977 [ "TIME_CHECK", [ "Valid" ] ], 978 [ "PWR_CONTROL", power_states/*[ "SystemOn" ]*/ ], 979 [ "AGILENT_CONTROL_24V", [ "VoltageOn" ] ], 980 [ "AGILENT_CONTROL_50V", [ "VoltageOn" ] ], 981 [ "AGILENT_CONTROL_80V", [ "VoltageOn" ] ], 982 [ "BIAS_CONTROL", [ "VoltageOff", "VoltageOn", "Ramping" ] ], 983 [ "FEEDBACK", [ "Calibrated", "InProgress", "OnStandby", "Warning", "Critical" ] ], 984 // [ "LID_CONTROL", [ "Open", "Closed" ] ], 985 [ "LID_CONTROL", [ "Open", "Closed", "Inconsistent" ] ], 986 [ "DRIVE_CONTROL", drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ], 987 [ "FTM_CONTROL", [ "Valid", "TriggerOn" ] ], 988 [ "FAD_CONTROL", [ "Connected", "RunInProgress" ] ], 989 [ "RATE_SCAN", [ "Connected" ] ], 990 [ "RATE_CONTROL", [ "Connected", "GlobalThresholdSet", "InProgress" ] ], 991 [ "GPS_CONTROL", [ "Locked" ] ], 992 [ "SQM_CONTROL", [ "Disconnected", "Connected", "Valid" ] ], 993 [ "PFMINI_CONTROL", [ "Disconnected", "Connected", "Receiving" ] ], 994 ]; 995 996 997 if (!checkStates(table)) 998 { 999 throw new Error("Something unexpected has happened. One of the servers "+ 1000 "is in a state in which it should not be. Please,"+ 1001 "try to find out what happened..."); 1002 } 1003 1004 datalogger_subscriptions.check(); 1005 1006 // If this is an observation which needs the voltage to be swicthed on 1007 // skip that if the voltage is not stable 1008 /* 1009 if (obs[sub].task=="DATA" || obs[sub].task=="RATESCAN") 1010 { 1011 var state = dim.state("FEEDBACK").name; 1012 if (state=="Warning" || state=="Critical" || state=="OnStandby") 1013 { 1014 v8.sleep(1000); 1015 continue; 1016 } 1017 }*/ 1018 1019 1020 // Check if obs.task is one of the one-time-tasks 1021 switch (obs[sub].task) 1022 { 1023 case "IDLE": 1024 v8.sleep(5000); 1025 continue; 1026 1027 case "SLEEP": 1028 Shutdown("sleep"); //GoToSleep(); 1029 1030 sub++; 1031 dim.log("Task finished [SLEEP]."); 1032 console.out(""); 1033 continue; 1034 1035 case "STARTUP": 1036 CloseLid(); 1037 1038 doDrsCalibration("startup"); // will switch the voltage off 1039 1040 if (irq) 1041 break; 1042 1043 service_feedback.voltageOn(); 1044 service_feedback.waitForVoltageOn(); 1045 1046 // Before we can switch to 3000 we have to make the right DRS calibration 1047 dim.log("Taking single p.e. run."); 1048 while (!irq && !takeRun("single-pe", 10000)); 1049 1050 // It is unclear what comes next, so we better switch off the voltage 1051 service_feedback.voltageOff(); 1052 1053 system_on = true; 1054 dim.log("Task finished [STARTUP]"); 1055 console.out(""); 1056 break; 1057 1058 case "SHUTDOWN": 1059 Shutdown("singlepe"); 1060 system_on = false; 1061 1062 // FIXME: Avoid new observations after a shutdown until 1063 // the next startup (set run back to -2?) 1064 sub++; 1065 dim.log("Task finished [SHUTDOWN]"); 1066 console.out(""); 1067 //console.out(" Waiting for next startup.", ""); 1068 continue; 1069 1070 case "DRSCALIB": 1071 doDrsCalibration("drscalib"); // will switch the voltage off 1072 dim.log("Task finished [DRSCALIB]"); 1073 console.out(""); 1074 break; 1075 1076 case "SINGLEPE": 1077 // The lid must be closes 1078 CloseLid(); 1079 1080 // Check if DRS calibration is necessary 1081 var diff = getTimeSinceLastDrsCalib(); 1082 if (diff>30 || diff==null) 1083 { 1084 doDrsCalibration("singlepe"); // will turn voltage off 1085 if (irq) 1086 break; 1087 } 1088 1089 // The voltage must be on 1090 service_feedback.voltageOn(); 1091 service_feedback.waitForVoltageOn(); 1092 1093 // Before we can switch to 3000 we have to make the right DRS calibration 1094 dim.log("Taking single p.e. run."); 1095 while (!irq && !takeRun("single-pe", 10000)); 1096 1097 // It is unclear what comes next, so we better switch off the voltage 1098 service_feedback.voltageOff(); 1099 dim.log("Task finished [SINGLE-PE]"); 1100 console.out(""); 1101 break; 1102 1103 case "OVTEST": 1104 var locked = dim.state("DRIVE_CONTROL").name=="Locked"; 1105 if (!locked) 1106 dim.send("DRIVE_CONTROL/PARK"); 1107 1108 dim.send("FEEDBACK/STOP"); 1109 1110 // The lid must be closed 1111 CloseLid(); 1112 1113 if (!locked) 1114 { 1115 //console.out("Waiting for telescope to park. This may take a while."); 1116 dim.wait("DRIVE_CONTROL", "Locked", 3000); 1117 dim.send("DRIVE_CONTROL/UNLOCK"); 1118 } 1119 1120 // Check if DRS calibration is necessary 1121 var diff = getTimeSinceLastDrsCalib(); 1122 if (diff>30 || diff==null) 1123 { 1124 doDrsCalibration("ovtest"); // will turn voltage off 1125 if (irq) 1126 break; 1127 } 1128 1129 // The voltage must be on 1130 service_feedback.voltageOn(0.4); 1131 service_feedback.waitForVoltageOn(); 1132 1133 dim.log("Taking single p.e. run (0.4V)"); 1134 while (!irq && !takeRun("single-pe", 10000)); 1135 1136 for (var i=5; i<18 && !irq; i++) 1137 { 1138 dim.send("FEEDBACK/STOP"); 1139 dim.wait("FEEDBACK", "Calibrated", 3000); 1140 dim.wait("BIAS_CONTROL", "VoltageOn", 3000); 1141 dim.send("FEEDBACK/START", i*0.1); 1142 dim.wait("FEEDBACK", "InProgress", 45000); 1143 dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000? 1144 service_feedback.waitForVoltageOn(); 1145 dim.log("Taking single p.e. run ("+(i*0.1)+"V)"); 1146 while (!irq && !takeRun("single-pe", 10000)); 1147 } 1148 1149 // It is unclear what comes next, so we better switch off the voltage 1150 service_feedback.voltageOff(); 1151 dim.log("Task finished [OVTEST]"); 1152 console.out(""); 1153 break; 1154 1155 case "RATESCAN": 1156 var tm1 = new Date(); 1157 1158 // This is a workaround to make sure that we really catch 1159 // the new OnTrack state later and not the old one 1160 dim.send("DRIVE_CONTROL/STOP"); 1161 dim.wait("DRIVE_CONTROL", "Initialized", 15000); 1162 1163 // The lid must be open 1164 OpenLid(); 1165 1166 // Switch the voltage to a reduced level (Ubd) 1167 service_feedback.voltageOn(0); 1168 1169 if (obs[sub].source != null) // undefined != null -> false 1170 { 1171 dim.log("Pointing telescope to '"+obs[sub].source+"'."); 1172 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source); 1173 } 1174 else 1175 { 1176 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec); 1177 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec); 1178 } 1179 1180 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing 1181 1182 // Now tracking stable, switch voltage to nominal level and wait 1183 // for stability. 1184 service_feedback.voltageOn(); 1185 service_feedback.waitForVoltageOn(); 1186 1187 if (!irq) 1188 { 1189 dim.log("Starting calibration."); 1190 1191 // Calibration (2% of 20') 1192 while (!irq) 1193 { 1194 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s 1195 continue; 1196 //if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s 1197 // continue; 1198 break; 1199 } 1200 1201 var tm2 = new Date(); 1202 1203 dim.log("Starting ratescan."); 1204 1205 //set reference to whole camera (in case it was changed) 1206 dim.send("RATE_SCAN/SET_REFERENCE_CAMERA"); 1207 // Start rate scan 1208 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10, "default"); 1209 1210 // Lets wait if the ratescan really starts... this might take a few 1211 // seconds because RATE_SCAN configures the ftm and is waiting for 1212 // it to be configured. 1213 dim.wait("RATE_SCAN", "InProgress", 10000); 1214 dim.wait("RATE_SCAN", "Connected", 2700000); 1215 1216 // Here one could implement a watchdog for the feedback as well, but what is the difference 1217 // whether finally one has to find out if the feedback was in the correct state 1218 // or the ratescan was interrupted? 1219 1220 // this line is actually some kind of hack. 1221 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time 1222 // So I decided to put this line here as a kind of patchwork.... 1223 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 1224 1225 dim.log("Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000)); 1226 } 1227 1228 dim.log("Task finished [RATESCAN]"); 1229 console.out(""); 1230 break; // case "RATESCAN" 1231 1232 case "RATESCAN2": 1233 var tm1 = new Date(); 1234 1235 // This is a workaround to make sure that we really catch 1236 // the new OnTrack state later and not the old one 1237 dim.send("DRIVE_CONTROL/STOP"); 1238 dim.wait("DRIVE_CONTROL", "Initialized", 15000); 1239 1240 if (obs[sub].rstype=="dark-bias-off") 1241 service_feedback.voltageOff(); 1242 else 1243 { 1244 // Switch the voltage to a reduced level (Ubd) 1245 var bias = dim.state("BIAS_CONTROL").name; 1246 if (bias=="VoltageOn" || bias=="Ramping") 1247 service_feedback.voltageOn(0); 1248 } 1249 1250 // Open the lid if required 1251 if (!obs[sub].lidclosed) 1252 OpenLid(); 1253 else 1254 CloseLid(); 1255 1256 // track source/position or move to position 1257 if (obs[sub].lidclosed) 1258 { 1259 dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az); 1260 dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az); 1261 v8.sleep(3000); 1262 dim.wait("DRIVE_CONTROL", "Initialized", 150000); // 110s for turning and 30s for stabilizing 1263 } 1264 else 1265 { 1266 if (obs[sub].source != null) // undefined != null -> false 1267 { 1268 dim.log("Pointing telescope to '"+obs[sub].source+"'."); 1269 dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source); 1270 } 1271 else 1272 { 1273 dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec); 1274 dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec); 1275 } 1276 1277 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing 1278 } 1279 1280 // Now tracking stable, switch voltage to nominal level and wait 1281 // for stability. 1282 if (obs[sub].rstype!="dark-bias-off") 1283 { 1284 service_feedback.voltageOn(); 1285 service_feedback.waitForVoltageOn(); 1286 } 1287 1288 if (!irq) 1289 { 1290 var tm2 = new Date(); 1291 1292 dim.log("Starting ratescan 2/1 ["+obs[sub].rstype+"]"); 1293 1294 //set reference to whole camera (in case it was changed) 1295 dim.send("RATE_SCAN/SET_REFERENCE_CAMERA"); 1296 // Start rate scan 1297 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 300, 20, obs[sub].rstype); 1298 1299 // Lets wait if the ratescan really starts... this might take a few 1300 // seconds because RATE_SCAN configures the ftm and is waiting for 1301 // it to be configured. 1302 dim.wait("RATE_SCAN", "InProgress", 10000); 1303 //FIXME: discuss what best value is here 1304 dim.wait("RATE_SCAN", "Connected", 2700000);//45min 1305 //dim.wait("RATE_SCAN", "Connected", 1200000);//3.3h 1306 1307 // Here one could implement a watchdog for the feedback as well, but what is the difference 1308 // whether finally one has to find out if the feedback was in the correct state 1309 // or the ratescan was interrupted? 1310 1311 // this line is actually some kind of hack. 1312 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time 1313 // So I decided to put this line here as a kind of patchwork.... 1314 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 1315 1316 dim.log("Ratescan 2/1 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000)); 1317 } 1318 1319 if (!irq) 1320 { 1321 var tm2 = new Date(); 1322 1323 dim.log("Starting ratescan 2/2 ["+obs[sub].rstype+"]"); 1324 1325 // Start rate scan 1326 dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 300, 1000, 100, obs[sub].rstype); 1327 1328 // Lets wait if the ratescan really starts... this might take a few 1329 // seconds because RATE_SCAN configures the ftm and is waiting for 1330 // it to be configured. 1331 dim.wait("RATE_SCAN", "InProgress", 10000); 1332 dim.wait("RATE_SCAN", "Connected", 2700000); 1333 1334 // Here one could implement a watchdog for the feedback as well, but what is the difference 1335 // whether finally one has to find out if the feedback was in the correct state 1336 // or the ratescan was interrupted? 1337 1338 // this line is actually some kind of hack. 1339 // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time 1340 // So I decided to put this line here as a kind of patchwork.... 1341 //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6); 1342 1343 dim.log("Ratescan 2/2 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000)); 1344 } 1345 1346 dim.log("Task finished [RATESCAN2]"); 1347 console.out(""); 1348 break; // case "RATESCAN2" 1349 1350 case "CUSTOM": 1351 1352 // This is a workaround to make sure that we really catch 1353 // the new OnTrack state later and not the old one 1354 dim.send("DRIVE_CONTROL/STOP"); 1355 dim.wait("DRIVE_CONTROL", "Initialized", 15000); 1356 1357 // Ramp bias if needed 1358 if (!obs[sub].biason) 1359 service_feedback.voltageOff(); 1360 else 1361 { 1362 // Switch the voltage to a reduced level (Ubd) 1363 var bias = dim.state("BIAS_CONTROL").name; 1364 if (bias=="VoltageOn" || bias=="Ramping") 1365 service_feedback.voltageOn(0); 1366 } 1367 // Close lid 1368 CloseLid(); 1369 1370 // Move to position (zd/az) 1371 dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az); 1372 dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az); 1373 v8.sleep(3000); 1374 dim.wait("DRIVE_CONTROL", "Initialized", 150000); // 110s for turning and 30s for stabilizing 1375 1376 // Now tracking stable, switch voltage to nominal level and wait 1377 // for stability. 1378 if (obs[sub].biason) 1379 { 1380 service_feedback.voltageOn(); 1381 service_feedback.waitForVoltageOn(); 1382 } 1383 1384 if (!irq) 1385 { 1386 dim.log("Taking custom run with time "+obs[sub].time+"s, threshold="+obs[sub].threshold+", biason="+obs[sub].biason); 1387 1388 var customRun = function() 1389 { 1390 v8.sleep(500);//wait that configuration is set 1391 dim.wait("FTM_CONTROL", "TriggerOn", 15000); 1392 dim.send("FAD_CONTROL/SEND_SINGLE_TRIGGER"); 1393 dim.send("RATE_CONTROL/STOP"); 1394 dim.send("FTM_CONTROL/STOP_TRIGGER"); 1395 dim.wait("FTM_CONTROL", "Valid", 3000); 1396 dim.send("FTM_CONTROL/ENABLE_TRIGGER", true); 1397 dim.send("FTM_CONTROL/SET_TIME_MARKER_DELAY", 123); 1398 dim.send("FTM_CONTROL/SET_THRESHOLD", -1, obs[sub].threshold); 1399 v8.sleep(500);//wait that configuration is set 1400 dim.send("FTM_CONTROL/START_TRIGGER"); 1401 dim.wait("FTM_CONTROL", "TriggerOn", 15000); 1402 } 1403 1404 takeRun("custom", -1, obs[sub].time, customRun); 1405 } 1406 dim.log("Task finished [CUSTOM]."); 1407 dim.log(""); 1408 break; // case "CUSTOM" 1409 1410 case "DATA": 1411 1412 // ========================== case "DATA" ============================ 1413 /* 1414 if (Sun.horizon("FACT").isUp) 1415 { 1416 console.out(" SHUTDOWN",""); 1417 Shutdown(); 1418 console.out(" Exit forced due to broken schedule", ""); 1419 exit(); 1420 } 1421 */ 1422 1423 // Calculate remaining time for this observation in minutes 1424 var remaining = nextObs==undefined ? 0 : (nextObs.start-new Date())/60000; 1425 //dim.log("DEBUG: remaining: "+remaining+" nextObs="+nextObs+" start="+nextObs.start); 1426 1427 // ------------------------------------------------------------ 1428 1429 dim.log("Run count "+run+" [remaining "+parseInt(remaining)+"min]"); 1430 1431 // ----- Time since last DRS Calibration [min] ------ 1432 var diff = getTimeSinceLastDrsCalib(); 1433 1434 // Changine pointing position and take calibration... 1435 // ...every four runs (every ~20min) 1436 // ...if at least ten minutes of observation time are left 1437 // ...if this is the first run on the source 1438 var point = (run%4==0 && remaining>10 && !obs[sub].orbit) || run==0; // undefined==null -> true! 1439 1440 // Take DRS Calib... 1441 // ...every four runs (every ~20min) 1442 // ...at last every two hours 1443 // ...when DRS temperature has changed by more than 2deg (?) 1444 // ...when more than 15min of observation are left 1445 // ...no drs calibration was done yet 1446 var drscal = (run%4==0 && (remaining>15 && diff>70)) || diff==null; 1447 1448 if (point) 1449 { 1450 // Switch the voltage to a reduced voltage level 1451 service_feedback.voltageOn(0); 1452 1453 // Change wobble position every four runs, 1454 // start with alternating wobble positions each day 1455 var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1; 1456 var angle = obs[sub].angle == null ? Math.random()*360 : obs[sub].angle; 1457 1458 if (obs[sub].orbit) // != undefined, != null, != 0 1459 dim.log("Pointing telescope to '"+obs[sub].source+"' [orbit="+obs[sub].orbit+"min, angle="+angle+"]"); 1460 else 1461 dim.log("Pointing telescope to '"+obs[sub].source+"' [wobble="+wobble+"]"); 1462 1463 // This is a workaround to make sure that we really catch 1464 // the new OnTrack state later and not the old one 1465 dim.send("DRIVE_CONTROL/STOP"); 1466 dim.wait("DRIVE_CONTROL", "Initialized", 15000); 1467 1468 if (obs[sub].orbit) // != undefined, != null, != 0 1469 dim.send("DRIVE_CONTROL/TRACK_ORBIT", angle, obs[sub].orbit, obs[sub].source); 1470 else 1471 dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source); 1472 1473 // Do we have to check if the telescope is really moving? 1474 // We can cross-check the SOURCE service later 1475 } 1476 1477 if (drscal) 1478 { 1479 doDrsCalibration("data"); // will turn voltage off 1480 1481 // Now we switch on the voltage and a significant amount of 1482 // time has been passed, so do the check again. 1483 sun = Sun.horizon(-12); 1484 if (!was_up && sun.isUp) 1485 { 1486 dim.log("Sun rise detected...."); 1487 continue; 1488 } 1489 } 1490 1491 if (irq) 1492 continue; 1493 1494 OpenLid(); 1495 1496 // This is now th right time to wait for th drive to be stable 1497 dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing 1498 1499 // Now check the voltage... (do not start a lot of stuff just to do nothing) 1500 var state = dim.state("FEEDBACK").name; 1501 if (state=="Warning" || state=="Critical" || state=="OnStandby") 1502 { 1503 v8.sleep(60000); 1504 continue; 1505 } 1506 1507 // Now we are 'OnTrack', so we can ramp to nominal voltage 1508 // and wait for the feedback to get stable 1509 service_feedback.voltageOn(); 1510 service_feedback.waitForVoltageOn(); 1511 1512 // If pointing had changed, do calibration 1513 if (!irq && point) 1514 { 1515 dim.log("Starting calibration."); 1516 1517 // Calibration (2% of 20') 1518 while (!irq) 1519 { 1520 if (irq || !takeRun("pedestal", 1000)) // 80 Hz -> 10s 1521 continue; 1522 // if (irq || !takeRun("light-pulser-ext", 1000)) // 80 Hz -> 10s 1523 // continue; 1524 break; 1525 } 1526 } 1527 1528 //console.out(" Taking data: start [5min]"); 1529 1530 // FIXME: What do we do if during calibration something has happened 1531 // e.g. drive went to ERROR? Maybe we have to check all states again? 1532 1533 var twilight = Sun.horizon(-16).isUp; 1534 1535 if (twilight) 1536 { 1537 for (var i=0; i<5 && !irq; i++) 1538 takeRun("data", -1, 60); // Take data (1min) 1539 } 1540 else 1541 { 1542 var len = 300; 1543 while (!irq && len>15) 1544 { 1545 var time = new Date(); 1546 if (takeRun("data", -1, len)) // Take data (5min) 1547 break; 1548 1549 len -= parseInt((new Date()-time)/1000); 1550 } 1551 } 1552 1553 //console.out(" Taking data: done"); 1554 run++; 1555 1556 continue; // case "DATA" 1557 } 1558 1559 if (nextObs!=undefined && sub==obs.length-1) 1560 dim.log("Next observation will start at "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]"); 1561 1562 sub++; 1563 } 1564 1565 sub_drsruns.close(); 1566 1567 dim.log("Left main loop [irq="+irq+"]"); 1568 1569 // ================================================================ 1570 // Comments and ToDo goes here 1571 // ================================================================ 1572 1573 // error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/ 1574 // classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/ 1575