Index: trunk/FACT++/scripts/CheckFTU.js
===================================================================
--- trunk/FACT++/scripts/CheckFTU.js	(revision 14763)
+++ trunk/FACT++/scripts/CheckFTU.js	(revision 14763)
@@ -0,0 +1,41 @@
+'use strict';
+
+var service_ftm = new Subscription("FTM_CONTROL/FTU_LIST");
+
+// Make sure that we receive a 'Yes, we are connected' event
+service_ftm.get(5000);
+
+// Wait for the format string to be available
+// FIXME: Timeout
+while (!dim.send("FTM_CONTROL"))
+    v8.sleep();
+
+// Check for all FTUs to be connected when the next event arrives
+service_ftm.onchange = function(event)
+{
+    var ping = event.obj['Ping'];
+    for (var i=0; i<40; i++)
+    {
+        if (ping[i]==1)
+            continue;
+
+        console.out("Problems in the FTU communication found.");
+        console.out("Send command to disable all FTUs.");
+        console.out(" => Power cycle needed.");
+
+        dim.send("FTM_CONTOL/ENABLE_FTU", -1, false);
+        throw new Error("CrateReset[FTU]");
+    }
+
+    // Signal success by closing the connection
+    service_ftm.close();
+}
+
+// Send ping (request FTU status)
+dim.send("FTM_CONTROL/PING");
+
+// Wait for 1 second for the answer
+var timeout = new Thread(1000, function(){ if (service_ftm.isOpen) throw new Error(""); });
+while (service_ftm.isOpen)
+    v8.sleep();
+
Index: trunk/FACT++/scripts/CheckStates.js
===================================================================
--- trunk/FACT++/scripts/CheckStates.js	(revision 14763)
+++ trunk/FACT++/scripts/CheckStates.js	(revision 14763)
@@ -0,0 +1,299 @@
+'use strict';
+
+/**
+ *
+ *  Waits for the timeout (in ms) for the given servers to be online
+ *  and in one of the provided states.
+ *
+ *  Returns true if MAGIC_WEATHER is online, FTM_CONTROL is in Idle
+ *  and LID_CONTROL is either in Open or Closed.
+ *
+ *  Returns false if all given servers are online, but at least one is
+ *  not in the expected state.
+ *
+ *  Throws an exception if not all provided servers are online after
+ *  the given timeout.
+ *
+ *  If you want to wait infinitely: CheckStates(table, null)
+ *  If the timeout is negative (e.g. -500 means 500ms) or zero,
+ *     no exception is thrown but false is returned.
+ *
+ * @param table
+ *
+ * @param {Integer} [timeout=5000]
+ *    timeout in milliseconds
+ *
+ * @returns
+ *
+ * @throws
+ *
+ * @example
+ *    var table =
+ *    [
+ *        [ "MAGIC_WEATHER"  ],
+ *        [ "FTM_CONTROL",  [ "Idle" ] ],
+ *        [ "LID_CONTROL",  [ "Open", "Closed" ] ],
+ *    ];
+ *
+ *    checkStates(table);
+ *
+ *
+ */
+function checkStates(table, timeout)
+{
+    if (timeout===undefined)
+        timeout = 5000;
+
+    var states = [];
+
+    var time = new Date();
+    while (1)
+    {
+        // Get states of all servers in question
+        states = [];
+        for (var i=0; i<table.length; i++)
+        {
+            var state = dim.state(table[i][0]);
+            states[i] = state ? state.name : undefined;
+        }
+
+        // Check if they are all valid
+        if (states.indexOf(undefined)<0)
+        {
+            // If they are all valid, check them against the
+            // state lists provided for each server
+            var rc = true;
+            for (var i=0; i<table.length; i++)
+            {
+                if (!table[i][1] || table[i][1].indexOf(states[i])>=0)
+                    continue;
+
+                console.out(table[i][0]+" in ["+states[i]+"] not as it ought to be ["+table[i][1]+"]");
+                rc = false;
+            }
+
+            return rc;
+        }
+
+        if ((new Date())-time>=Math.abs(timeout))
+            break;
+
+        v8.sleep();
+    }
+
+    if (timeout<0)
+        return false;
+
+    // Create a list of all servers which do not have valid states yet
+    var servers = [];
+    for (var i=0; i<table.length; i++)
+        if (!states[i])
+            servers.push(table[i][0]);
+
+    // If all servers do not yet have valid states, it is obsolete to
+    // print all their names
+    if (servers.length==table.length && server.length>1)
+        servers = [ "servers." ];
+
+    throw new Error("Timeout waiting for access to named states of "+servers.join(", "));
+}
+
+function checkSend(servers, timeout)
+{
+    if (timeout===undefined)
+        timeout = 5000;
+
+    var states = [];
+
+    var time = new Date();
+    while (1)
+    {
+        // Get states of all servers in question
+        states = [];
+        for (var i=0; i<servers.length; i++)
+            states[i] = dim.send(servers[i]);
+
+        // Check if they are all valid
+        if (states.indexOf(false)<0)
+            return true;
+
+        if ((new Date())-time>=Math.abs(timeout))
+            break;
+
+        v8.sleep();
+    }
+
+    if (timeout<0)
+        return false;
+
+    // Create a list of all servers which do not have valid states yet
+    var servers = [];
+    for (var i=0; i<servers.length; i++)
+        if (!states[i])
+            servers.push(servers[i]);
+
+    throw new Error("Timeout waiting for send-ready of "+servers.join(", "));
+}
+
+function Wait(server,states,timeout1,timeout2)
+{
+    if (typeof(states)=="string")
+        states = [ states ];
+
+    // If timeout2 is defined and >0 wait first for the
+    // server to come online. If it does not come online
+    // in time, an exception is thrown.
+    if (timout2>0 && CheckStates([ server ], timeout2))
+        return true;
+
+    var time = new Date();
+    while (1)
+    {
+        // If a server disconnects now while checking for the
+        // states, an exception will be thrown, too.
+        if (CheckStates([ server, states ], 0))
+            return true;
+
+        if (Date()-time>=abs(timeout1))
+            break;
+
+        v8.sleep();
+    }
+
+    if (timeout1<0)
+        throw new Error("Timeout waiting for Server "+server+" to be in ["+states+"]");
+
+    return false;
+}
+
+// Wait 5s for the FAD_CONTROL to get to the states
+//   return false if FAD_CONTROL is not online
+//   return false if state is not
+// Wait("FAD_CONTROL", [ "COnnected", "Disconnected" ], 5000);
+
+function dimwait(server, state, timeout)
+{
+    if (!timeout)
+        timeout = 5000;
+
+    var time = new Date();
+    while (1)
+    {
+        var s = dim.state(server);
+        if (s.index===state || s.name===state)
+            return true;
+
+        //if (s.index==undefined)
+        //    throw "Server "+server+" not connected waiting for "+state+".";
+
+        if (Date()-time>=timeout)
+            break;
+
+        v8.sleep();
+    }
+
+    if (timeout>0)
+        throw new Error("Timeout waiting for ["+states+"] of "+server+".");
+
+    return false;
+
+
+    /*
+    if (!timeout)
+        timeout = 5000;
+
+    var time = new Date();
+    while (timeout<0 || new Date()-time<timeout)
+    {
+        var s = dim.state(server);
+        if (s.index===state || s.name===state)
+            return true;
+
+        if (s.index==undefined)
+            throw "Server "+server+" not connected waiting for "+state+".";
+
+        v8.sleep();
+    }
+
+    return false;
+*/
+}
+
+function Sleep(timeout)
+{
+    if (!timeout)
+        timeout = 5000;
+
+    var time = new Date();
+    while (Date()-time<timeout)
+        v8.sleep();
+}
+
+function Timer()
+{
+    this.date = new Date();
+
+    this.reset = function() { this.date = new Date(); }
+    this.print = function(id)
+    {
+        var diff = Date()-this.date;
+        if (id)
+            console.out("Time["+id+"]: "+diff+"ms");
+        else
+            console.out("Time: "+diff+"ms");
+    }
+}
+
+function WaitStates(server, states, timeout, func)
+{
+    var save = dim.onchange[server];
+
+    function inner()
+    {
+        dim.onchange[server] = function(arg)
+        {
+            if (!this.states)
+                this.states = states instanceof Array ? states : [ states ];
+
+            var comp = this.states[0];
+            if (arg.index===comp || arg.name===comp || comp=='*')
+                this.states.shift();
+
+            //console.out(JSON.stringify(arg), this.states, comp, arg.name, "");
+
+            if (save instanceof Function)
+                save();
+
+            if (this.states.length==0)
+                delete dim.onchange[server];
+
+        }
+
+        if (func instanceof Function)
+            func();
+
+        var time = new Date();
+        while (1)
+        {
+            if (!dim.onchange[server])
+                return true;
+
+            if (new Date()-time>=timeout)
+                break;
+
+            v8.sleep();
+        }
+
+        delete dim.onchange[server];
+
+        if (timeout>0)
+            throw new Error("Timeout waiting for ["+states+"] of "+server+".");
+
+        return false;
+    }
+
+    var rc = inner();
+    dim.onchang
+        e[server] = save;
+    return rc;
+}
Index: trunk/FACT++/scripts/CheckUnderflow.js
===================================================================
--- trunk/FACT++/scripts/CheckUnderflow.js	(revision 14763)
+++ trunk/FACT++/scripts/CheckUnderflow.js	(revision 14763)
@@ -0,0 +1,187 @@
+'use strict';
+
+var Func = function() { };
+Func.sum = function(a, b) { return a+b; }
+Func.sq  = function(a, b) { return Math.sqrt(a*a + b*b); }
+Func.min = function(a, b) { return Math.min(a, b); }
+Func.max = function(a, b) { return Math.max(a, b); }
+Func.avg = function(arr)  { return arr.reduce(Func.Sum, 0)/arr.length; }
+Func.stat = function(arr, func)
+{
+    if (arr.length==0)
+        return undefined;
+
+    var sum = 0;
+    var sq  = 0;
+    var cnt = 0;
+    var min = arr[0];
+    var max = arr[0];
+    arr.forEach(function(val) { sum+=val; sq+=val*val; if (val>max) max=val; if (val<min) min=val; if (func && func(val)) cnt++ });
+    sum /= arr.length;
+    sq  /= arr.length;
+
+    return { avg:sum, rms:Math.sqrt(sq-sum*sum), min:min, max:max, count:cnt };
+}
+
+// ===================================================================
+
+console.out(("\n%78s".$("")).replace(/ /g, "="));
+
+include('scripts/CheckStates.js');
+
+var table =
+[
+ [ "MCP",             [ "Idle"      ] ],
+ [ "AGILENT_CONTROL", [ "VoltageOn" ] ],
+ [ "FTM_CONTROL",     [ "Idle"      ] ],
+ [ "FAD_CONTROL",     [ "Connected",    "WritingData"     ] ],
+ [ "BIAS_CONTROL",    [ "Disconnected", "VoltageOff"      ] ],
+ [ "DATA_LOGGER",     [ "WaitForRun",   "NightlyFileOpen", "Logging" ] ],
+];
+
+console.out("Checking states.");
+if (!checkStates(table))
+{
+    console.out("Something unexpected has happened. One of the servers",
+            "is in a state in which it should not be. Please,",
+            "try to find out what happened...");
+    exit();
+}
+
+// ===================================================================
+
+include('scripts/Hist1D.js');
+include('scripts/Hist2D.js');
+
+console.out("Checking power on time");
+
+var service_drs = new Subscription("FAD_CONTROL/DRS_RUNS");
+
+var runs = service_drs.get(5000, false);
+if (!runs)
+    throw new Error("Could not connect to FAD_CONTROL/DRS_RUNS");
+/*
+runs = {
+    "name"    : "FAD_CONTROL/DRS_RUNS",
+    "format"  : "I:1;I:3",
+    "qos"     : 0,
+    "size"    : 16,
+    "counter" : 0,
+    "time"    : new Date("2012-11-20T05:23:52.804Z"),
+    "data"    : [300,[148,149,154]]
+  };
+*/
+
+// Wait for agilent to be online with state information
+// FIXME: timeout
+while (!dim.state("AGILENT_CONTROL"))
+    v8.sleep();
+
+var power = dim.state("AGILENT_CONTROL").time;
+var now   = new Date();
+
+var diff = (now-runs.time)/3600000;
+
+console.out(" * Now:                "+now);
+console.out(" * Last power cycle:   "+power);
+console.out(" * Last DRS calib set: "+runs.time);
+//console.out(JSON.stringify(dim.state("AGILENT_CONTROL")));
+
+
+if (1)//diff>8 && now.getHours()>16 || runs.time<power)
+{
+    //console.out("Most probablay the camera has not been checked for underflows yet.");
+
+    // Wait for dim.send to be ready
+    // FIXME: timeout
+    while (!dim.send("FAD_CONTROL"))
+        v8.sleep();
+
+    // FAD_CONTROL/RAW_DATA:
+    //  roi,roi_tm,num_fad,num_ftm,type,num_boards,error,
+    //  dummy,time,time_board,start_pix,start_tm,adc
+
+    // FAD_CONTROL/EVENT_DATA
+    // avg,rms,max,pos
+
+    var service_event = new Subscription("FAD_CONTROL/EVENT_DATA");
+    var event = service_event.get(5000, false);
+    //if (!event)
+    //    throw new Error("Could not connect to FAD_CONTROL/EVENT_DATA");
+
+    var event_counter = event.counter;
+
+    dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
+    dim.send("FAD_CONTROL/SET_FILE_FORMAT", 0);
+
+    console.out("Starting drs-gain");
+    dim.send("MCP/START", -1, 3, "drs-gain");
+
+    // FIXME: add a timeout here (10s)
+    console.out("Waiting for new event");
+    var d = new Date();
+    while (event_counter == service_event.get(0, false).counter)
+        v8.sleep();
+
+    console.out("Event received: "+(new Date()-d)+"ms");
+
+    console.out("Stop run");
+    dim.send("MCP/STOP");
+    while (!dim.send("RATE_CONTROL"))
+    {
+        v8.sleep(800);
+        console.out("waiting for RATE_CONTROL to send the necessary format information");
+    }
+    dim.send("RATE_CONTROL/STOP"); // GlobalThresholdSet -> Connected
+    dim.wait("MCP", "Idle", 3000);
+
+    var nn = runs.data[0]>0 ? runs.data[1].reduce(Func.max) : -1;
+    if (nn>0)
+    {
+        console.out("Trying to restore last DRS calibration #"+nn+"  ["+runs.time+"]");
+
+        var night = new Date(runs.time-1000*60*60*12);
+        var yy = night.getUTCFullYear();
+        var mm = night.getUTCMonth()+1;
+        var dd = night.getUTCDate();
+
+        var filefmt = "/loc_data/raw/%d/%02d/%02d/%4d%02d%02d_%03d.drs.fits";
+
+        // FIXME: Timeout
+        var drs_counter = service_drs.get(0, false);
+        dim.send("FAD_CONTROL/LOAD_DRS_CALIBRATION", filefmt.$(yy, mm, dd, yy, mm, dd, nn));
+        while (drs_counter == service_drs.get(0, false).counter)
+            v8.sleep();
+    }
+
+    //console.out(JSON.stringify(service_drs.get(0, false)));
+
+    event = service_event.get(0, false);
+
+    service_event.close();
+
+    var hist = Hist2D(16, -2048.5, 2048.5, 11, -10, 100);
+
+    for (var i=0; i<1440; i++)
+        hist.fill(event.data[0][i], isNaN(event.data[1][i])?-1:event.data[1][i]);
+
+    //console.out(("%78s".$("")).replace(/ /g, "-"));
+    hist.print();
+    //console.out(("%78s".$("")).replace(/ /g, "-"));
+
+    // Could also be <512
+    var stat0 = Func.stat(event.data[0], function(val) { if (val<0) console.out(" VAL="+val); return val<0; });
+    var stat1 = Func.stat(event.data[1]);
+
+    console.out("Avg[min]=%.1f".$(stat0.min));
+    console.out("Avg[avg]=%.1f +- %.1f".$(stat0.avg, stat0.rms));
+    console.out("Avg[max]=%.1f".$(+stat0.max));
+    console.out("Avg[cnt]="+stat0.count);
+    console.out("");
+    console.out("Rms[min]=%.1f".$(stat1.min));
+    console.out("Rms[avg]=%.1f +- %.1f".$(stat1.avg, stat1.rms));
+    console.out("Rms[max]=%.1f".$(stat1.max));
+    console.out(("%78s\n".$("")).replace(/ /g, "="));
+}
+
+service_drs.close();
Index: trunk/FACT++/scripts/Handler.js
===================================================================
--- trunk/FACT++/scripts/Handler.js	(revision 14763)
+++ trunk/FACT++/scripts/Handler.js	(revision 14763)
@@ -0,0 +1,48 @@
+'use strict';
+
+function Handler(name)
+{
+    this.name  = name;
+    this.array = [];
+
+    this.add = function(func)
+    {
+        this.array.push(func);
+        console.out(this.name+":add [N="+this.array.length+"]");
+    }
+
+    this.run = function(timeout)
+    {
+        console.out(this.name+":start");
+
+        var rc = [];
+
+        var start = new Date();
+        while (!timeout || (new Date()-start)<timeout)
+        {
+            var done = true;
+
+            //rc = rc.map(this.array[i]);
+            //
+            //rc.forEach(function(el){ done &= el && el.length==0;);
+
+            for (var i=0; i<this.array.length; i++)
+            {
+                rc[i] = this.array[i](rc[i]);
+                if (rc[i]===undefined || rc[i].length>0)
+                    done = false;
+            }
+
+            if (done)
+            {
+                console.out(this.name+":success [time="+(new Date()-start)+"ms]");
+                return true;
+            }
+
+            v8.sleep();
+        }
+
+        console.out(this.name+":timeout ["+timeout+"ms]");
+        return false;
+    }
+}
Index: trunk/FACT++/scripts/Hist1D.js
===================================================================
--- trunk/FACT++/scripts/Hist1D.js	(revision 14763)
+++ trunk/FACT++/scripts/Hist1D.js	(revision 14763)
@@ -0,0 +1,109 @@
+/**
+ * @fileOverview A simple one dimensional histogram.
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+'use strict';
+
+/**
+ *
+ * @constructor
+ *
+ * @param {Interger} nx
+ *
+ * @param {Number} xmin
+ *
+ * @param {Number} xmax
+ *
+ * @returns
+ *     A sub-classed array with the Hist1D functions added as properties.
+ *
+ * @example
+ *     var hist = Hist1D(10, -0.5, 1.5);
+ *
+ */
+function Hist1D(nx, xmin, xmax)
+{
+    /**
+     *
+     * Array
+     *
+     */
+    var arr = new Array(nx);
+
+    /**
+     *
+     * @exports arr.get as Hist1D.get
+     *
+     */
+    arr.get = function(x)
+    {
+        var ix = parseInt(nx*(x-xmin)/(xmax-xmin));
+
+        return arr[ix] ? arr[ix] : 0;
+    }
+
+    /**
+     *
+     * @exports arr.fill as Hist1D.fill
+     *
+     */
+    arr.fill = function(x, w)
+    {
+        if (!x || x===NaN)
+            return false;
+
+        var ix = parseInt(nx*(x-xmin)/(xmax-xmin));
+        if (ix<0 || ix>=nx)
+            return false;
+
+        if (!arr[ix])
+            arr[ix] = 0;
+
+        arr[ix] += w ? w : 1;
+
+        return true;
+    }
+
+    /**
+     *
+     * @exports arr.print as Hist1D.print
+     *
+     */
+    arr.print = function(len)
+    {
+        if (!len)
+            len = 40;
+        if (len<6)
+            len = 6;
+
+        var sum = arr.reduce(function(a,b){return a+b;}, 0);
+        var max = arr.reduce(function(a,b){return Math.max(a,b);}, 0);
+
+        console.out("");
+        for (var ix=nx-1; ix>=0; ix--)
+        {
+            var entry = arr[ix] ? arr[ix] : 0;
+
+            var line ="%3d [%3d%] ".$(ix, sum==0 ? 0 : 100*entry/sum);
+            if (arr[ix])
+            {
+                var val = parseInt(len*arr[ix]/max);
+                var entry = ("%"+val+"s").$("");
+                entry = entry.replace(/ /g, "*");
+                line += ("%-"+len+"s").$(entry)+" |";
+            }
+
+            console.out(line);
+        }
+
+        var entry = arr[ix] ? arr[ix] : 0;
+        console.out("   --------"+("%"+(len-5)+"s").$("")+"-------");
+
+        var line =" %9d  ".$(sum);
+        line += ("%"+len+"d").$(max);
+        console.out(line);
+        console.out("");
+    }
+
+    return arr;
+}
Index: trunk/FACT++/scripts/Hist2D.js
===================================================================
--- trunk/FACT++/scripts/Hist2D.js	(revision 14763)
+++ trunk/FACT++/scripts/Hist2D.js	(revision 14763)
@@ -0,0 +1,90 @@
+'use strict';
+
+function Hist2D(nx, xmin, xmax, ny, ymin, ymax)
+{
+    var arr = new Array(nx);
+
+    arr.get = function(x, y)
+    {
+        var ix = parseInt(nx*(x-xmin)/(xmax-xmin));
+        var iy = parseInt(ny*(y-ymin)/(ymax-ymin));
+
+        if (!arr[ix])
+            return 0;
+
+        if (!arr[ix][iy])
+            return 0;
+
+        return arr[ix][iy];
+    }
+
+    arr.fill = function(x, y, w)
+    {
+        var ix = parseInt(nx*(x-xmin)/(xmax-xmin));
+        var iy = parseInt(ny*(y-ymin)/(ymax-ymin));
+
+        if (ix<0 || ix>=nx || iy<0 || iy>=ny)
+            return false;
+
+        if (!arr[ix])
+            arr[ix] = new Array(ny);
+
+        if (!arr[ix][iy])
+            arr[ix][iy] = 0;
+
+        arr[ix][iy] += w ? w : 1;
+
+        return true;
+    }
+
+    arr.print = function()
+    {
+        var line1 = "   |";
+        for (var ix=0; ix<nx; ix++)
+            line1 += " %3d ".$(ix);
+
+        var line2 = "---+";
+        for (var ix=0; ix<nx; ix++)
+            line2 += "+----";
+        line2+="+----";
+
+        console.out("", line1, line2);
+
+        var sum = 0;
+        var sx = [];
+        for (var ix=0; ix<nx; ix++)
+            sx[ix] = 0;
+
+        for (var iy=ny-1; iy>=0; iy--)
+        {
+            var line = "%3d|".$(iy);
+
+            var sy = 0;
+        for (var ix=0; ix<nx; ix++)
+            {
+                var val = arr[ix] ? arr[ix][iy] : "";
+                line += " %4s".$(val?val:"");
+
+                if (arr[ix])
+                {
+                    sy     += val?val:0;
+                    sx[ix] += val?val:0;
+                }
+            }
+
+            sum += sy;
+
+            console.out(line+"|%4d".$(sy));
+        }
+
+        console.out(line2);
+
+        line = "   |";
+        for (var ix=0; ix<nx; ix++)
+            line += " %4d".$(sx[ix]);
+
+        console.out(line+"|%4d".$(sum), "");
+    }
+
+    return arr;
+}
Index: trunk/FACT++/scripts/Main.js
===================================================================
--- trunk/FACT++/scripts/Main.js	(revision 14763)
+++ trunk/FACT++/scripts/Main.js	(revision 14763)
@@ -0,0 +1,674 @@
+/**
+ * @fileOverview This file has functions related to documenting JavaScript.
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+'use strict';
+
+
+dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
+
+
+// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
+// clases: http://www.phpied.com/3-ways-to-define-a-javascript-class/
+//
+// Arguments: TakeFirstDrsCalib
+// To be determined: How to stop the script without foreceful interruption?
+
+// adapt states of drivectrl
+
+//
+// SCRIPTS:
+//
+//  - Startup, e.g. do not open LID
+//
+//  - Bring telescop to state 'operational', e.g. open lid,
+//    when first run != START is detected in the loop
+//
+//  - Take data
+//
+//  - Shutdown
+//
+// ----> Does smartfact display alarm messages already?
+// ----> How to display errors in smartfact? Is that necessary at all?
+//       (red output? maybe just <red>...</red>?
+//
+// ----------------------------------------------------------------
+
+function currentEst(source)
+{
+    var moon = new Moon();
+    if (!moon.isUp)
+        return 7.7;
+
+    var dist = Sky.dist(moon, source);
+
+    var alt = 90-moon.toLocal().zd;
+
+    var lc = dist*alt*pow(Moon.disk(), 6)/360/360;
+
+    var cur = 7.7+4942*lc;
+
+    return cur;
+}
+
+function thresholdEst(source) // relative threshold (ratio)
+{
+    // Assumption:
+    // atmosphere is 70km, shower taks place after 60km, earth radius 6400km
+    // just using the cosine law
+    // This fits very well with MC results: See Roger Firpo, p.45
+    // "Study of the MAGIC telescope sensitivity for Large Zenith Angle observations"
+
+    var c = Math.cos(Math.Pi-source.zd);
+    var ratio = (10*sqrt(409600*c*c+9009) + 6400*c - 60)/10;
+
+    // assumption: Energy threshold increases linearily with current
+    // assumption: Energy threshold increases linearily with distance
+
+    return ratio*currentEst(source)/7.7;
+}
+
+// Ratio in rate would be (estimate, not precise calculation)
+// pow(ratio, -0.7)
+
+// ----------------------------------------------------------------
+
+
+var service_con = new Subscription("FAD_CONTROL/CONNECTIONS");
+service_con.onchange = function(evt)
+{
+    this.reset = [ ];
+
+    for (var x=0; x<40; x++)
+        if (evt.obj['status'][x]!=66 && evt.obj['status'][x]!=67)
+            this.reset.push(x);
+
+    if (this.reset.length==0)
+        return;
+
+    dim.alarm("FAD board loss detected...");
+    dim.send("MCP/RESET");
+    dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+}
+
+service_con.reconnect = function()
+{
+    if (this.reset.length==0)
+        return true;
+
+    console.out("  Reconnect: start ["+this.reset.length+"]");
+
+    for (var i=0; i<this.reset.length; i++)
+        dim.send("FAD_CONTROL/DISCONNECT", this.reset[i]);
+
+    v8.sleep(3000);
+
+    while (this.reset.length)
+        dim.send("FAD_CONTROL/CONNECT", this.reset.pop());
+
+    v8.sleep(1000);
+    dim.wait("FAD_CONTROL", "Connected", 3000);
+
+    console.out("  Reconnect: end");
+
+    return true;
+}
+
+function takeRun(type, count, time)
+{
+    if (!count)
+        count = -1;
+    if (!time)
+        time = -1;
+
+    console.out("  Take run N="+count+" T="+time+"s ["+type+"]");
+    // change rats for cal runs1!!!
+
+    dim.send("MCP/START", time?time:-1, count?count:-1, type);
+
+    // What could be a reasonable timeout here?
+    // FIXME: Replace by callback?
+    if (!dim.wait("MCP", "TakingData", -300000) )
+    {
+        console.out("MCP took longer than 5 minutes to start TakingData");
+        console.out("maybe this idicates a problem with one of the FADs?");
+        dimctrl.setState(37);
+        dim.wait("MCP", "TakingData", 500);
+
+    }
+    dim.wait("MCP", "Idle");
+
+    console.out("  Take run: end");
+
+    if (!service_con.reconnect())
+        exit();
+
+    return true;//service_con.reconnect();
+}
+
+// ----------------------------------------------------------------
+
+function doDrsCalibration()
+{
+    console.out("  DRS cal: start");
+    service_feedback.voltageOff();
+
+    while (1)
+    {
+        dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
+        if (!takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+        if (!takeRun("drs-gain",     1000))     // 40 / 20s     (50Hz)
+            continue;
+        if (!takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
+        if (!takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+        if (!takeRun("drs-time",     1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
+        if (!takeRun("pedestal",     1000))     // 40 / 10s     (80Hz)
+            continue;
+
+        dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
+        if (!takeRun("pedestal",     1000))     // 40 / 10s     (80Hz)
+            continue;
+        //                                       -----------
+        //                                       4'40 / 2'00
+
+        break;
+    }
+
+    console.out("  DRS cal: end");
+}
+
+// ----------------------------------------------------------------
+
+function OpenLid()
+{
+    var horizon_parameter = "nautical"
+    if (Sun.horizon( horizon_parameter ).isUp)
+    {
+        console.out(JSON.stringify(Sun.horizon( horizon_parameter )));
+        throw new Error("Sun is above FACT-horizon, lid cannot be opened.");
+    }
+
+    console.out("Open lid: start");
+
+    // Wait for lid to be open
+    if (dim.state("LID_CONTROL").name=="Closed")
+        dim.send("LID_CONTROL/OPEN");
+    dim.wait("LID_CONTROL", "Open", 30000);
+    console.out("Open lid: done");
+}
+
+function CloseLid()
+{
+    console.out("Close lid: start");
+
+    // Wait for lid to be open
+    if (dim.state("LID_CONTROL").name=="Open")
+        dim.send("LID_CONTROL/CLOSE");
+    dim.wait("LID_CONTROL", "Closed", 30000);
+
+    console.out("Open lid: end");
+}
+
+// ----------------------------------------------------------------
+
+var service_feedback = new Subscription("FEEDBACK/DEVIATION");
+
+service_feedback.voltageOn = function()
+{
+    //if (Sun.horizon("FACT").isUp)
+    //    throw new Error("Sun is above FACT-horizon, voltage cannot be switched on.");
+
+    console.out("  Voltage on: start");
+
+    var isOff = dim.state("BIAS_CONTROL").name=="VoltageOff";
+
+    if (isOff)
+    {
+        console.out("  Voltage on: switch on");
+        console.out(JSON.stringify(dim.state("BIAS_CONTROL")));
+
+        dim.send("BIAS_CONTROL/SET_GLOBAL_DAC", 1);
+    }
+
+    // Wait until voltage on
+    dim.wait("BIAS_CONTROL", "VoltageOn", 5000);
+
+    // From now on the feedback waits for a valid report from the FSC
+    // and than switchs to CurrentControl
+    dim.wait("FEEDBACK", "CurrentControl", 60000);
+
+    if (isOff)
+    {
+        this.cnt = this.get().counter;
+        console.out("  Voltage on: cnt="+this.cnt);
+    }
+
+    console.out("  Voltage on: end");
+}
+
+service_feedback.voltageOff = function()
+{
+    console.out("  Voltage off: start");
+
+    var isOn = dim.state("BIAS_CONTROL").name=="VoltageOn";
+
+    if (isOn)
+    {
+        console.out("  Voltage on: switch off");
+        dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+    }
+
+    dim.wait("BIAS_CONTROL", "VoltageOff", 5000);
+
+    // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
+    // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
+
+    console.out("  Voltage off: end");
+}
+
+
+service_feedback.waitForVoltageOn = function()
+{
+    // waiting 45sec for the current control to stabilize...
+    // v8.sleep(45000);
+
+    // ----- Wait for at least three updates -----
+    // The feedback is started as if the camera where at 0deg
+    // Then after the first temp update, the temperature will be set to the
+    // correct value (this has already happened)
+    // So we only have to wait for the current to get stable.
+    // This should happen after three to five current updates.
+    // So we want one recent temperature update
+    //  and three recent current updates
+    console.out("  Voltage wait: start");
+    while (this.cnt==undefined || this.get().counter<=this.cnt+2)
+        v8.sleep();
+    console.out("  Voltage wait: end [cnt="+this.get().counter+"]");
+}
+
+// ================================================================
+// Crosscheck all states
+// ================================================================
+
+include('scripts/Startup.js');//Startup();
+
+/*
+include('scripts/CheckStates.js');
+
+var table =
+[
+ [ "TNG_WEATHER"   ],
+ [ "MAGIC_WEATHER" ],
+ [ "CHAT"          ],
+ [ "SMART_FACT"     ],
+ [ "FSC_CONTROL",     [ "Connected"                    ] ],
+ [ "MCP",             [ "Idle"                         ] ],
+ [ "TIME_CHECK",      [ "Valid"                        ] ],
+ [ "PWR_CONTROL",     [ "SystemOn"                     ] ],
+ [ "AGILENT_CONTROL", [ "VoltageOn"                    ] ],
+ [ "BIAS_CONTROL",    [ "VoltageOff"                   ] ],
+ [ "FEEDBACK",        [ "CurrentControl", "CurrentCtrlIdle", "Connected" ] ],
+ [ "RATE_SCAN",       [ "Connected"                    ] ],
+ [ "RATE_CONTROL",    [ "Connected"                    ] ],
+ [ "LID_CONTROL",     [ "Open", "Closed"               ] ],
+ [ "DRIVE_CONTROL",   [ "Armed", "Tracking", "OnTrack" ] ],
+ [ "FTM_CONTROL",     [ "Idle", "TriggerOn"            ] ],
+ [ "FAD_CONTROL",     [ "Connected", "WritingData"     ] ],
+ [ "DATA_LOGGER",     [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
+];
+
+console.out("Checking states.");
+if (!checkStates(table, 10000))
+{
+    console.out("Something unexpected has happened. Although the startup-",
+            "procedure has finished, not all servers are in the state",
+            "in which they ought to be. Please, try to find out what",
+            "happened...");
+    exit();
+}
+
+console.out("Checking states: done.");
+*/
+// ----------------------------------------------------------------
+
+console.out("Checking send.");
+checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
+console.out("Checking send: done");
+
+// ----------------------------------------------------------------
+
+console.out("Feedback init: start.");
+service_feedback.get(5000);
+
+dim.send("FEEDBACK/ENABLE_OUTPUT", true);
+dim.send("FEEDBACK/START_CURRENT_CONTROL", 0.);
+
+console.out("Feedback init: end.");
+
+// ----------------------------------------------------------------
+// ================================================================
+// ----------------------------------------------------------------
+
+// All times are in UTC
+var observations =
+[
+ [ "2013-01-07 19:28", "STARTUP" ],
+ [ "2013-01-07 21:00", "Crab"],
+ [ "2013-01-08 05:00", "Mrk 421" ],
+ [ "2013-01-08 07:00", "SHUTDOWN"],
+];
+
+//observations[-1] = { start:now, source:"END", toString:function(){ return "END ["+this.start+"]" } };
+
+//
+// Convert time given in UTC to correct Date object (be aware
+// the Date object will be in local time!)
+//
+// FIXME: Check transisiton from summer- and winter-time!!
+//
+for (var i=0; i<observations.length; i++)
+{
+    var utc = new Date(observations[i][0]+" UTC");
+    if (isNaN(utc.valueOf()))
+        throw new Error("Not a valid date '"+observations[i][0]+"' in row "+i);
+    if (i>0 && utc<=observations[i-1][0])
+        throw new Error("Start time '"+utc.toUTCString()+"' in row "+i+" exceeds start time in row "+(i-1));
+    //observations[i][0] = utc;
+    observations[i] = { start:utc, source:observations[i][1], toString:function(){ return this.source+" ["+this.start+"]" } };
+}
+
+// Get the observation scheduled for 'now' from the table and
+// retunr its index
+function getObservation(now)
+{
+    if (now==undefined)
+        now = new Date();
+
+    if (isNaN(now.valueOf()))
+        throw new Error("Date argument in getObservation invalid.");
+
+    for (var i=0; i<observations.length; i++)
+        if (now<observations[i].start)
+            return i-1;
+
+    return observations.length-1;
+}
+
+// ----------------------------------------------------------------
+// Bring the system into a well defined state
+// ----------------------------------------------------------------
+
+console.out("Drs runs init: start.");
+
+// FIMXE: Double subscription is not allowed!
+//        also Startup needs DRS_RUNS
+var service_drs = new Subscription("FAD_CONTROL/DRS_RUNS");
+service_drs.get(5000, false);
+
+console.out("Drs runs init: end.");
+
+// FIXME: Check if the last DRS calibration was complete?
+// ----------------------------------------------------------------
+
+// We have to stup a few things here which might not by set by Startup.js
+dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
+
+// ----------------------------------------------------------------
+
+console.out("Start main loop.");
+
+function Startup()
+{
+    /**** dummy ****/
+    console.out("  => [STARTUP] called.");
+}
+
+function Shutdown()
+{
+    /**** dummy ****/
+    console.out("  => [SHUTDOWN] called.");
+}
+
+var run = -2;
+var lastObs;
+
+while (1)
+{
+    // Check if observation position is still valid
+    // If source position has changed, set run=0
+    var idxObs = getObservation();
+    if (idxObs===undefined)
+        exit();
+
+    if (idxObs==-1)
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    var obs     = observations[idxObs];
+    var nextObs = observations[idxObs+1];
+
+    // Check if observation target has changed
+    if (lastObs!=idxObs)
+    {
+        console.out("--- "+idxObs+" ---");
+        console.out("Current time:        "+new Date());
+        console.out("Current observation: "+obs);
+        console.out("Next    observation: "+nextObs);
+        console.out("");
+
+        // This is the first source, but we do not come from
+        // a scheduled 'START', so we have to check if the
+        // telescop is operational already
+        if (run==-2)
+        {
+            Startup();   // -> Bias On/Off?, Lid open/closed?
+            CloseLid();
+        }
+
+        run = 0;
+    }
+    lastObs = idxObs;
+
+    // We have performed startup or shutdown... wait for next observation
+    if (run==-1)
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    // Check (once) if startup of shutdown is scheduled
+    switch (obs.source)
+    {
+    case "STARTUP":
+        console.out("  STARTUP", "");
+        Startup();  // BiasOn/Off?, Lid open/close?
+        CloseLid();
+
+        console.out("  Take DRS calibration.");
+        doDrsCalibration();  // -> VoltageOff
+
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        // Before we can switch to 3000 we have to make the right DRS calibration
+        console.out("  Take single p.e. run.");
+        while (!takeRun("pedestal", 5000));
+
+        service_feedback.voltageOff();
+
+        console.out("  Waiting for first scheduled observation.","");
+        run = -1;
+        continue;
+
+    case "SHUTDOWN":
+        console.out("  SHUTDOWN","");
+        Shutdown();
+
+        console.out("  Waiting for next startup.", "");
+        run = -1;
+        continue;
+
+    case "RATESCAN":
+        console.out("  RATESCAN not yet implemented.");
+        continue;
+    }
+/*
+    if (Sun.horizon("FACT").isUp)
+    {
+        console.out("  SHUTDOWN","");
+        Shutdown();
+        console.out("  Exit forced due to broken schedule", "");
+        exit();
+    }
+*/
+    // Calculate remaining time for this observation in minutes
+    var remaining = (nextObs.start-new Date())/60000;
+
+    // ------------------------------------------------------------
+
+    console.out("  Checking states [mainloop]");
+    var table =
+        [
+         [ "TNG_WEATHER"   ],
+         [ "MAGIC_WEATHER" ],
+         [ "CHAT"          ],
+         [ "SMART_FACT"    ],
+         [ "DATA_LOGGER",     [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
+         [ "FSC_CONTROL",     [ "Connected"                    ] ],
+         [ "MCP",             [ "Idle"                         ] ],
+         [ "TIME_CHECK",      [ "Valid"                        ] ],
+         [ "PWR_CONTROL",     [ "SystemOn"                     ] ],
+         [ "AGILENT_CONTROL", [ "VoltageOn"                    ] ],
+         [ "BIAS_CONTROL",    [ "VoltageOff", "VoltageOn"                   ] ],
+         [ "FEEDBACK",        [ "CurrentCtrlIdle", "CurrentControl" ] ],
+         [ "RATE_SCAN",       [ "Connected"                    ] ],
+         [ "RATE_CONTROL",    [ "Connected", "InProgress"                    ] ],
+         [ "LID_CONTROL",     [ "Open", "Closed"               ] ],
+         [ "DRIVE_CONTROL",   [ "Armed", "Tracking", "OnTrack" ] ],
+         [ "FTM_CONTROL",     [ "Idle", "TriggerOn"            ] ],
+         [ "FAD_CONTROL",     [ "Connected", "WritingData"     ] ],
+        ];
+
+    if (!checkStates(table))
+    {
+        throw new Error("Something unexpected has happened. One of the servers"+
+                        "is in a state in which it should not be. Please,"+
+                        "try to find out what happened...");
+    }
+
+    console.out("  Checking states: end.");
+
+    // ------------------------------------------------------------
+
+    console.out("  Run #"+run+"  ("+parseInt(remaining)+"min)");
+
+    // ----- Time since last DRS Calibration [min] ------
+    var runs = service_drs.get(0, false);
+    var diff = (new Date()-runs.time)/60000;
+
+    // Warning: 'roi=300' is a number which is not intrisically fixed
+    //          but can change depending on the taste of the observers
+    var valid = runs.data[1][2]>0 && runs.data[0]==300;
+
+    if (valid)
+        console.out("  Last DRS calib: "+diff+"min ago");
+    else
+        console.out("  No valid drs calibration");
+
+    // Changine pointing position and take calibration...
+    //  ...every four runs (every ~20min)
+    //  ...if at least ten minutes of observation time are left
+    //  ...if this is the first run on the source
+    var point  = (run%4==0 && remaining>10) || run==0;
+
+    // Take DRS Calib...
+    //  ...every four runs (every ~20min)
+    //  ...at last  every two hours
+    //  ...when DRS temperature has changed by more than 2deg (?)
+    //  ...when more than 15min of observation are left
+    //  ...no drs calibration was done yet
+    var drscal = (run%4==0 && (remaining>15 && diff>70)) || !valid;
+
+    if (point)
+    {
+        var wobble = parseInt(run/4)%2;
+
+        //console.out("  Move telescope to '"+source+"' "+offset+" "+wobble);
+        console.out("  Move telescope to '"+obs.source+"' ["+wobble+"]");
+
+        //var offset = observations[obs][2];
+        //var wobble = observations[obs][3 + parseInt(run/4)%2];
+
+        //dim.send("DRIVE_CONTROL/TRACK_SOURCE", offset, wobble, source);
+
+        dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble+1, obs.source);
+
+        // Do we have to check if the telescope is really moving?
+        // We can cross-check the SOURCE service later
+    }
+
+    if (drscal)
+    {
+        console.out("  Take DRS calibration.");
+        doDrsCalibration();  // -> VoltageOff
+    }
+
+    OpenLid();
+
+    // voltage must be switched on after the lid is open for the
+    // feedback to adapt the voltage properly to the night-sky
+    // background light level.
+    service_feedback.voltageOn();
+
+    // This is now th right time to wait for th drive to be stable
+    dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+
+    // Now we have to be prepared for data-taking:
+    // make sure voltage is on
+    service_feedback.waitForVoltageOn();
+
+    // If pointing had changed, do calibration
+    if (point)
+    {
+        console.out("  Calibration.");
+
+        // Calibration (2% of 20')
+        while (1)
+        {
+            if (!takeRun("pedestal",         1000))  // 80 Hz  -> 10s
+                continue;
+            if (!takeRun("light-pulser-ext", 1000))  // 80 Hz  -> 10s
+                continue;
+            break;
+        }
+    }
+
+    console.out("  Taking data: start [5min]");
+
+    var len = 300;
+    while (len>0)
+    {
+        var time = new Date();
+        if (!takeRun("data", -1, len)) // Take data (5min)
+            len -= parseInt((new Date()-time)/1000);
+        else
+            break;
+    }
+
+    //v8.sleep(360000);
+    console.out("  Taking data: done");
+
+    run++;
+}
+
+service_drs.close();
Index: trunk/FACT++/scripts/Startup.js
===================================================================
--- trunk/FACT++/scripts/Startup.js	(revision 14763)
+++ trunk/FACT++/scripts/Startup.js	(revision 14763)
@@ -0,0 +1,288 @@
+'use strict';
+
+// To de done:
+//  - CheckLID status (should be open or closed)
+//  - Is it necessary to switch the bias-voltage off?
+//  - Get reasonable timeouts for all steps (wait, get, run)
+//  - Improve order to accelerate execution
+//
+// =================================================================
+
+/*
+var table =
+[
+ [ "AGILENT_CONTROL" ],
+ [ "BIAS_CONTROL"    ],
+ [ "CHAT"            ],
+ [ "DATA_LOGGER"     ],
+ [ "DRIVE_CONTROL"   ],
+ [ "FEEDBACK"        ],
+ [ "FAD_CONTROL"     ],
+ [ "FSC_CONTROL"     ],
+ [ "FTM_CONTROL"     ],
+ [ "LID_CONTROL"     ],
+ [ "MAGIC_WEATHER"   ],
+ [ "MCP"             ],
+ [ "PWR_CONTROL"     ],
+ [ "RATE_CONTROL"    ],
+ [ "RATE_SCAN"       ],
+ [ "SMART_FACT"      ],
+ [ "TIME_CHECK"      ],
+ [ "TNG_WEATHER"     ],
+];
+
+if (dim.state("DRIVE_CONTROL").name=="Locked")
+{
+    throw new Error("Drivectrl still locked... needs UNLOCK first.");
+    //while (!dim.send("DRIVE_CONTROL"))
+    //    v8.sleep();
+    //dim.send("DRIVE_CONTROL/UNLOCK");
+    //dim.wait("DRIVE_CONTROL", "Armed", 1000);
+}
+
+*/
+
+console.out("");
+
+var loop;
+include("scripts/Handler.js");
+include("scripts/CheckStates.js");
+
+// -----------------------------------------------------------------
+// Make sure camera electronics is switched on and has power
+// -----------------------------------------------------------------
+
+include("scripts/handleAgilentPowerOn.js");
+include("scripts/handlePwrCameraOn.js");
+
+checkSend(["AGILENT_CONTROL","PWR_CONTROL"]);
+
+loop = new Handler("PowerOn");
+loop.add(handleAgilentPowerOn);
+loop.add(handlePwrCameraOn);
+loop.run();
+console.out("");
+
+// If power was switched on: wait for a few seconds
+
+// -----------------------------------------------------------------
+// Now take care that the bias control, the ftm and the fsc are
+// properly connected and are in a reasonable state (e.g. the
+// trigger is switched off)
+// -----------------------------------------------------------------
+
+include("scripts/handleBiasVoltageOff.js");
+include("scripts/handleFtmIdle.js");
+include("scripts/handleFscConnected.js");
+include("scripts/handleFeedbackConnected.js");
+include("scripts/handleRatectrlConnected.js");
+include("scripts/handleLidClosed.js");
+
+checkSend(["BIAS_CONTROL","FTM_CONTROL", "FSC_CONTROL", "FEEDBACK", "RATE_CONTROL"]);
+
+// Missing MCP --> Idle
+loop = new Handler("SystemSetup");
+loop.add(handleBiasVoltageOff);
+loop.add(handleFtmIdle);
+loop.add(handleFscConnected);
+loop.add(handleFeedbackConnected); // Feedback needs FAD to be Connected
+loop.add(handleRatectrlConnected);
+//loop.add(handleLidClosed);
+loop.run();
+
+console.out("Bias crate connected and voltage off.");
+console.out("Feedback switched off.");
+console.out("FTM connected and idle.");
+console.out("FSC connected.");
+console.out("Rate control connected.");
+//console.out("Lids closed.");
+console.out("");
+
+console.out("Enable all FTU");
+dim.send("FTM_CONTROL/ENABLE_FTU", -1, true);
+
+// -----------------------------------------------------------------
+// Now we check the FTU connection
+// -----------------------------------------------------------------
+
+/*
+include("scripts/handleFtuCheck.js");
+
+loop = new Handler("FtuCheck");
+loop.ftuList = new Subscription("FTM_CONTROL/FTU_LIST");
+loop.add(handleFtuCheck);
+loop.run();
+loop.ftuList.close();
+
+dim.print("All FTUs are enabled and without error.");
+*/
+
+console.out("Checking FTU: start");
+include("scripts/CheckFTU.js");
+console.out("Checking FTU: done");
+console.out("");
+
+// -----------------------------------------------------------------
+// Now we can safely try to connect the FAD boards.
+// -----------------------------------------------------------------
+
+// If FADs already connected
+include("scripts/handleFadConnected.js");
+
+checkSend(["FAD_CONTROL"]);
+
+loop = new Handler("ConnectFad");
+loop.add(handleFadConnected);
+loop.run();
+
+var failed = false;
+dim.onchange["FAD_CONTROL"] = function(arg)
+{
+    if (this.rc && arg.name!="Connected")
+        failed = true;
+}
+
+console.out("FADs connected.");
+console.out("");
+
+console.out(dim.state("FAD_CONTROL").name);
+console.out(dim.state("MCP").name);
+
+// ================================================================
+// Underflow check
+// ================================================================
+// Is it necessary to check for the so called 'underflow-problem'?
+// (This is necessary after each power cycle)
+// ----------------------------------------------------------------
+
+include('scripts/CheckUnderflow.js');
+
+// Now it is time to check the connection of the FADs
+// it might hav thrown an exception already anyway
+
+
+// ================================================================
+// Power on drive system if power is off (do it hre to make sure not
+// everything is switchd on at the same time)
+// ================================================================
+
+console.out("PWR: "+(dim.state("PWR_CONTROL").index&16));
+
+if ((dim.state("PWR_CONTROL").index&16)==0)
+{
+    console.out("Drive on");
+    dim.send("PWR_CONTROL/TOGGLE_DRIVE");
+}
+    include("scripts/handleDriveArmed.js");
+
+    checkSend(["DRIVE_CONTROL"]);
+
+    loop = new Handler("ArmDrive");
+    loop.add(handleDriveArmed);
+    loop.run();
+
+
+// ================================================================
+// Bias crate calibration
+// ================================================================
+// Bias crate calibration if necessary (it is aftr 4pm (local tome)
+// and the last calibration was more than eight hours ago.
+// -----------------------------------------------------------------
+
+// At this point we know that:
+//  1) The lid is closed
+//  2) The feedback is stopped
+//  3) The voltage is off
+
+// Check age of calibration
+var service_calibration = new Subscription("FEEDBACK/CALIBRATION");
+
+var data_calibration = service_calibration.get(-5000);
+//if (!data_calibration)
+//    throw new Error("Could not connect to FEEDBACK/CALIBRATION");
+
+function makeCurrentCalibration( reason_string  )
+{
+     console.out( reason_string );
+ 
+     dim.send("BIAS_CONTROL/SET_GLOBAL_DAC", 1);
+     dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+ 
+     var now = new Date();
+     dim.send("FEEDBACK/CALIBRATE_CURRENTS");
+
+     // FIXME: Timeout!
+     console.out("Wait for calibration to start");
+     dim.wait("FEEDBACK", "Calibrating", 5000);
+
+     console.out("Wait for calibration to end");
+     dim.wait("FEEDBACK", "Connected", 60000);
+
+     console.out("Calibration took "+(new Date()-now)+"ms");
+
+     console.out("Wait for voltage to be off");
+     dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+     dim.wait("BIAS_CONTROL", "VoltageOff", 5000);
+}
+
+if (!data_calibration)
+{
+   // FEEDBACK might just be freshly started and 
+   // will not yet serve this service.
+   var reason_msg = "No current calibration avaliable. New calibration needed.";
+   makeCurrentCalibration( reason_msg );
+}
+else 
+{
+    var age = data_calibration.time;
+    var now = new Date();
+
+    //service_calibration.close();
+
+    var diff = (now-age)/3600000;
+
+    if ((diff>8 && now.getHours()>16)/* || empty*/)
+    {
+        var reason_msg = "Last BIAS crate calibration taken at "+age.toUTCString()+": New calibration needed.";
+        makeCurrentCalibration( reason_msg );
+    }
+}
+
+
+
+
+// ================================================================
+// Crosscheck all states
+// ================================================================
+
+var table =
+[
+ [ "TNG_WEATHER"   ],
+ [ "MAGIC_WEATHER" ],
+ [ "CHAT"          ],
+ [ "SMART_FACT"    ],
+ [ "DATA_LOGGER",     [ "NightlyFileOpen", "WaitForRun"   ] ],
+ [ "FSC_CONTROL",     [ "Connected"                       ] ],
+ [ "MCP",             [ "Idle"                            ] ],
+ [ "TIME_CHECK",      [ "Valid"                           ] ],
+ [ "PWR_CONTROL",     [ "SystemOn"                        ] ],
+ [ "AGILENT_CONTROL", [ "VoltageOn"                       ] ],
+ [ "BIAS_CONTROL",    [ "VoltageOn", "VoltageOff"         ] ],
+ [ "FEEDBACK",        [ "Connected"                       ] ],
+ [ "RATE_SCAN",       [ "Connected"                       ] ],
+ [ "RATE_CONTROL",    [ "Connected"                       ] ],
+ [ "LID_CONTROL",     [ "Open", "Closed"                  ] ],
+ [ "DRIVE_CONTROL",   [ "Armed", "Tracking", "OnTrack"    ] ],
+ [ "FTM_CONTROL",     [ "Idle", "TriggerOn"               ] ],
+ [ "FAD_CONTROL",     [ "Connected", "WritingData"        ] ],
+];
+
+if (!checkStates(table))
+{
+    throw new Error("Something unexpected has happened. Although the startup-"+
+                    "procedure has finished, not all servers are in the state"+
+                    "in which they ought to be. Please, try to find out what"+
+                    "happened...");
+}
+
+
Index: trunk/FACT++/scripts/handleAgilentPowerOn.js
===================================================================
--- trunk/FACT++/scripts/handleAgilentPowerOn.js	(revision 14763)
+++ trunk/FACT++/scripts/handleAgilentPowerOn.js	(revision 14763)
@@ -0,0 +1,35 @@
+// switch agilent control output on
+function handleAgilentPowerOn(wait_state)
+{
+    var state = dim.state("AGILENT_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("AGILENT_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+    case "Connected":
+        return undefined;
+
+    case "VoltageLow":
+        return wait_state;
+
+    case "VoltageOff":
+        console.out("Agilent in VoltageOff... switching agilent output on.");
+        dim.send("AGILENT_CONTROL/SET_POWER", true);
+        return "VoltageOn";
+
+    case "VoltageOn":
+        return "";
+
+    case "VoltageHigh":
+        throw new Error("Agilent reports voltage above limit... please check.");
+    }
+
+    throw new Error("AGILENT_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleBiasVoltageOff.js
===================================================================
--- trunk/FACT++/scripts/handleBiasVoltageOff.js	(revision 14763)
+++ trunk/FACT++/scripts/handleBiasVoltageOff.js	(revision 14763)
@@ -0,0 +1,44 @@
+// Get bias control connected and voltage off
+function handleBiasVoltageOff(wait_state)
+{
+    var state = dim.state("BIAS_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("BIAS_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "Connecting":
+    case "Initializing":
+    case "Connected":
+    case "Ramping":
+        return wait_state;
+
+    // Do-something conditions
+    case "Disconnected":
+        console.out("Bias in Diconnected... connect.");
+        dim.send("BIAS_CONTROL/RECONNECT");
+        return "VoltageOff";
+
+    case "NotReferenced":
+    case "VoltageOn":
+        console.out("Bias in "+state.name+"... switch voltage off.");
+        dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+        return "VoltageOff";
+
+    // Final state reached condition
+    case "VoltageOff":
+        return "";
+
+    // Conditions which cannot be handled
+    case "OverCurrent": throw "BIAS_CONTROL in OverCurrent";
+    case "ExpertMode":  throw "BIAS_CONTROL in expert mode";
+    }
+
+    throw new Error("BIAS_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleDriveArmed.js
===================================================================
--- trunk/FACT++/scripts/handleDriveArmed.js	(revision 14763)
+++ trunk/FACT++/scripts/handleDriveArmed.js	(revision 14763)
@@ -0,0 +1,47 @@
+// Get Drive control armed
+function handleDriveArmed(wait_state)
+{
+    var state = dim.state("DRIVE_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    dim.print("DRIVE_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+    case "Connected":
+    case "NotReady":
+    case "Ready":
+        v8.sleep(1000);
+        return undefined;
+
+    case "Locked":
+        throw new Error("Drive is LOCKED. Please unlock manually.");
+        //return undefined;
+        //dim.print("Drive in Locked... unlock.");
+        //dim.send("DRIVE_CONTROL/UNLOCK");
+        //return "Armed";
+
+    case "Moving":
+    case "Tracking":
+    case "OnTrack":
+        dim.print("Drive moving... stop.");
+        dim.send("DRIVE_CONTROL/STOP");
+        return "Armed";
+
+    case "ERROR":
+        dim.print("Drive in error... trying to reinitialize.");
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 5000);
+        return undefined;  // Process that again if necessary
+
+    case "Armed":
+        return "";
+    }
+
+    throw new Error("DRIVE_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleFadConnected.js
===================================================================
--- trunk/FACT++/scripts/handleFadConnected.js	(revision 14763)
+++ trunk/FACT++/scripts/handleFadConnected.js	(revision 14763)
@@ -0,0 +1,42 @@
+// Get fad control connected
+function handleFadConnected(wait_state)
+{
+    var state = dim.state("FAD_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("BIAS_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Offline":
+        return undefined;
+
+    case "Disconnected":
+        console.out("Fad in Diconnected... connect.");
+        dim.send("FAD_CONTROL/START");
+        return "Connected";
+
+    // Do-nothing conditions
+    case "Connecting":
+        return wait_state;
+
+    // Do-something conditions
+    case "Configuring1":
+    case "Configuring2":
+    case "Configured":
+        console.out("Bias in Configure state... reset.");
+        dim.send("FAD_CONTROL/RESET_CONFIGURE");
+        return "Connected";
+
+    // Final state reached condition
+    case "Connected":
+    case "WritingData":
+        return "";
+    }
+
+    throw new Error("FAD_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleFeedbackConnected.js
===================================================================
--- trunk/FACT++/scripts/handleFeedbackConnected.js	(revision 14763)
+++ trunk/FACT++/scripts/handleFeedbackConnected.js	(revision 14763)
@@ -0,0 +1,37 @@
+function handleFeedbackConnected(wait_state)
+{
+    var state = dim.state("FEEDBACK");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("FEEDBACK:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+    case "Connecting":
+    case "ConnectedFSC":
+    case "ConnectedFAD":
+        return undefined;
+
+    case "Connected":
+        return "";
+
+    case "FeedbackIdle":
+    case "TempCtrlIdle":
+    case "CurrentCtrlIdle":
+    case "FeedbackControl":
+    case "TempControl":
+    case "CurrentControl":
+    case "Calibrating":
+        console.out("Feedback in "+state.name+"... stop feedback.");
+        dim.send("FEEDBACK/STOP");
+        return "Connected";
+    }
+
+    throw new Error("FEEDBACK:"+state.name+"["+state.index+"] unknown or not handled.");
+}
+
Index: trunk/FACT++/scripts/handleFscConnected.js
===================================================================
--- trunk/FACT++/scripts/handleFscConnected.js	(revision 14763)
+++ trunk/FACT++/scripts/handleFscConnected.js	(revision 14763)
@@ -0,0 +1,26 @@
+// Get FSC connected
+function handleFscConnected(wait_state)
+{
+    var state = dim.state("FSC_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //console.out("FSC_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "Disconnected":
+        console.out("Fsc in Diconnected... connect.");
+        dim.send("FSC_CONTROL/RECONNECT");
+        return "Connected";
+
+    case "Connected":
+        return "";
+    }
+
+    throw new Error("FSC_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleFtmIdle.js
===================================================================
--- trunk/FACT++/scripts/handleFtmIdle.js	(revision 14763)
+++ trunk/FACT++/scripts/handleFtmIdle.js	(revision 14763)
@@ -0,0 +1,44 @@
+// Get ftm connected, idle and with working FTUs
+function handleFtmIdle(wait_state)
+{
+    var state = dim.state("FTM_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    // Only try to open the service if the server is already in the list
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("FTM_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+        console.out("Ftm in "+state.name+"... connect.");
+        dim.send("FTM_CONTROL/RECONNECT");
+        return "Idle";
+
+    case "Idle":
+        //ftu_counter = data.counter;
+        return "";
+
+    case "TriggerOn":
+        console.out("Ftm in "+state.name+"... stop trigger.");
+        dim.send("FTM_CONTROL/STOP_TRIGGER");
+        return "Idle";
+ 
+    case "Configuring1":
+    case "Configuring2":
+    case "Configured":
+        console.out("Ftm in "+state.name+"... reset configuration.");
+        dim.send("FTM_CONTROL/RESET_CONFIGURE");
+        return "Idle";
+
+    case "ConfigError1":
+    case "ConfigError2":
+    case "ConfigError3":
+        throw new Error("FTM_CONTROL:"+state.name+"["+state.index+"] in error state.");
+    }
+
+    throw new Error("FTM_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleFtuCheck.js
===================================================================
--- trunk/FACT++/scripts/handleFtuCheck.js	(revision 14763)
+++ trunk/FACT++/scripts/handleFtuCheck.js	(revision 14763)
@@ -0,0 +1,30 @@
+function handleFtuCheck(wait_state)
+{
+    var service = this.ftuList.get();
+    if (!service.obj || service.obj.length==0)
+        return undefined;
+
+    if (!wait_state)
+    {
+        dim.send("FTM_CONTOL/PING");
+        return toString(service.counter);
+    }
+
+    if (toInt(wait_state)==service.counter)
+        return wait_state;
+
+    var ping = service.data['Ping'];
+    for (var i=0; i<40; i++)
+    {
+        if (ping[i]==1)
+            continue;
+
+        dim.print("Problems in the FTU communication found.");
+        dim.print("Send command to disable all FTUs.");
+        dim.print(" => Power cycle needed.");
+        dim.send("FTM_CONTOL/ENABLE_FTU", -1, false);
+        throw new Error("CrateReset[FTU]");
+    }
+
+    return "";
+}
Index: trunk/FACT++/scripts/handleLidClosed.js
===================================================================
--- trunk/FACT++/scripts/handleLidClosed.js	(revision 14763)
+++ trunk/FACT++/scripts/handleLidClosed.js	(revision 14763)
@@ -0,0 +1,33 @@
+// Get Lids closed
+function handleLidClosed(wait_state)
+{
+    var state = dim.state("LID_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("LID_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "NoConnection":
+    case "Connected":
+    case "Unknown":
+    case "Moving":
+        return wait_state;
+
+    case "Inconsistent":
+    case "Open":
+        console.out("Lid in "+state.name+"... closing lids.");
+        dim.send("LID_CONTROL/CLOSE");
+        return "Closed";
+
+    case "Closed":
+        return "";
+    }
+
+    throw new Error("LID_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handlePwrCameraOn.js
===================================================================
--- trunk/FACT++/scripts/handlePwrCameraOn.js	(revision 14763)
+++ trunk/FACT++/scripts/handlePwrCameraOn.js	(revision 14763)
@@ -0,0 +1,49 @@
+// Switch interlock camera power on
+function handlePwrCameraOn(wait_state)
+{
+    var state = dim.state("PWR_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("PWR_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "Disconnected":
+    case "Connected":
+        return undefined;
+
+    // Drive off
+    case "PowerOff":
+        console.out("Pwr in PowerOff... switching camera power on.");
+        dim.send("PWR_CONTROL/CAMERA_POWER", true);
+        return "DriveOff";
+
+    // Drive on
+    case "DriveOn":
+        console.out("Pwr in DriveOn... switching camera power on.");
+        dim.send("PWR_CONTROL/CAMERA_POWER", true);
+        return "SystemOn";
+
+    // Intermediate states?
+    case "CameraOn":
+    case "BiasOn":
+    case "CameraOff":
+    case "BiasOff":
+        return wait_state;
+
+    case "DriveOff":
+    case "SystemOn":
+        // Now the agilent control need to be switched on!
+        return "";
+
+    case "CoolingFailure":
+        throw new Error("Cooling unit reports failure... please check.");
+    }
+
+    throw new Error("PWR_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: trunk/FACT++/scripts/handleRatectrlConnected.js
===================================================================
--- trunk/FACT++/scripts/handleRatectrlConnected.js	(revision 14763)
+++ trunk/FACT++/scripts/handleRatectrlConnected.js	(revision 14763)
@@ -0,0 +1,30 @@
+function handleRatectrlConnected(wait_state)
+{
+    var state = dim.state("RATE_CONTROL");
+    if (state===undefined)
+        return undefined;
+
+    if (wait_state && wait_state.length>0 && state.name!=wait_state)
+        return wait_state;
+
+    //dim.print("FEEDBACK:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Diconnected":
+        return undefined;
+
+    case "Calibrating":
+    case "GlobalThresholdSet":
+    case "InProgress":
+        console.out("Ratectrl in "+state.name+"... stop.");
+        dim.send("RATE_CONTROL/STOP");
+        return "Connected";
+
+    case "Connected":
+        return "";
+    }
+
+    throw new Error("RATE_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
+
