Index: branches/FACT++_scripts_refactoring/CheckFTU.js
===================================================================
--- branches/FACT++_scripts_refactoring/CheckFTU.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/CheckFTU.js	(revision 18221)
@@ -0,0 +1,47 @@
+'use strict';
+
+var service_ftm = new Subscription("FTM_CONTROL/FTU_LIST");
+
+// Make sure that we receive a 'Yes, we are connected and names are available' event
+service_ftm.get(5000);
+
+// 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;
+
+        var str = "";
+        for (var h=0; h<4; h++)
+        {
+            for (var w=0; w<10; w++)
+                str += ping[h*10+w];
+            if (h!=3)
+                str += '|';
+        }
+
+        console.out(str)
+
+        console.out("Problems in the FTU communication found.");
+        console.out("Send command to disable all FTUs.");
+        console.out(" => Crate reset needed.");
+
+        dim.send("FTM_CONTROL/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(3000, function(){ if (service_ftm.isOpen) throw new Error("Could not check that all FTUs are ok within 3s."); });
+while (service_ftm.isOpen)
+    v8.sleep();
+
Index: branches/FACT++_scripts_refactoring/CheckStates.js
===================================================================
--- branches/FACT++_scripts_refactoring/CheckStates.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/CheckStates.js	(revision 18221)
@@ -0,0 +1,304 @@
+'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, wait)
+{
+    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;
+
+                if (!wait)
+                    dim.log(table[i][0]+" in ["+states[i]+"] not as it ought to be ["+table[i][1]+"]");
+
+                rc = false;
+            }
+            if (rc)
+                return rc;
+
+            if (!wait)
+                return false;
+        }
+
+        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 && servers.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 missing = [];
+    for (var i=0; i<servers.length; i++)
+        if (!states[i])
+            missing.push(servers[i]);
+
+    throw new Error("Timeout waiting for send-ready of "+missing.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: branches/FACT++_scripts_refactoring/CheckUnderflow.js
===================================================================
--- branches/FACT++_scripts_refactoring/CheckUnderflow.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/CheckUnderflow.js	(revision 18221)
@@ -0,0 +1,243 @@
+'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, idx) { sum+=val; sq+=val*val; if (val>max) max=val; if (val<min) min=val; if (func && func(val, idx)) 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, "="));
+
+if (dim.state("FTM_CONTROL").name=="TriggerOn")
+{
+    dim.send("FTM_CONTROL/STOP_TRIGGER");
+    dim.wait("FTM_CONTROL", "Valid");
+}
+
+
+include('scripts/CheckStates.js');
+
+var table =
+[
+ [ "MCP",                 [ "Idle"      ] ],
+ [ "AGILENT_CONTROL_24V", [ "VoltageOn" ] ],
+ [ "AGILENT_CONTROL_50V", [ "VoltageOn" ] ],
+ [ "AGILENT_CONTROL_80V", [ "VoltageOn" ] ],
+ [ "FTM_CONTROL",         [ "Valid"     ] ],
+ [ "FAD_CONTROL",         [ "Connected",    "RunInProgress"   ] ],
+ [ "BIAS_CONTROL",        [ "Disconnected", "VoltageOff"      ] ],
+ [ "DATA_LOGGER",         [ "WaitForRun",   "NightlyFileOpen", "Logging" ] ],
+];
+
+console.out("Checking states.");
+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...");
+}
+
+// ===================================================================
+
+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");
+
+var power = dim.state("AGILENT_CONTROL_50V").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.data?runs.time:"none"));
+
+
+if (1)//diff>8 && now.getHours()>16 || runs.time<power)
+{
+    console.out("Checking send.");
+    checkSend(["FAD_CONTROL", "MCP", "RATE_CONTROL"]);
+    console.out("Checking send: done");
+
+    //console.out("Most probablay the camera has not been checked for underflows yet.");
+
+    var service_event = new Subscription("FAD_CONTROL/EVENT_DATA");
+
+    dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
+    dim.send("FAD_CONTROL/SET_FILE_FORMAT", 0);
+
+    var sub_runs = new Subscription("FAD_CONTROL/RUNS");
+    var sruns = sub_runs.get(5000, false);
+
+    if (dim.state("FAD_CONTROL").name=="RunInProgress" || sruns.qos==1)
+    {
+        dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+        dim.wait("FAD_CONTROL", "Connected", 3000);
+
+        console.out("Waiting for open files to be closed...");
+        v8.timeout(60000, function() { if (sub_runs.get(0, false).qos==0) return true; });
+
+        // Although the file should be closed now, the processing might still be on-going
+        // and delayed events might be received. The only fix for that issue is to
+        // add the run number to the data we are waiting for
+        v8.sleep(5000);
+    }
+
+    sub_runs.close();
+
+    console.out("Starting drs-gain... waiting for new event");
+
+    var sub_startrun = new Subscription("FAD_CONTROL/START_RUN");
+    var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
+    var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
+    sub_connections.get(5000);
+    sub_startrun.get(5000);
+
+    include('scripts/takeRun.js');
+
+    while (1)
+    {
+        var event_counter = service_event.get(10000, false).counter;
+
+        var stop = function ()
+        {
+            while (1)
+            {
+                if (dim.state("MCP").name=="TakingData" && service_event.get(0, false).counter>event_counter)
+                {
+                    dim.send("MCP/STOP");
+                    console.out("Sent MCP/STOP.");
+                    return;
+                }
+                v8.sleep(100);
+            }
+        }
+
+        var thread = new Thread(250, stop);
+
+        var rc = takeRun("drs-gain");
+
+        thread.kill();
+
+        if (rc)
+            break;
+    }
+
+    console.out("Event received.");
+
+    sub_incomplete.close();
+    sub_connections.close();
+    sub_startrun.close();
+
+
+    // FIXME: Restore DRS calibration in case of failure!!
+    //        FAD Re-connect in case of failure?
+    //        MCP/RESET in case of failure?
+    //        Proper error reporting!
+
+    var event = service_event.get(3000);//, false);
+    service_event.close();
+
+    console.out("Run stopped.");
+
+    dim.send("RATE_CONTROL/STOP"); // GlobalThresholdSet -> Connected
+    dim.wait("MCP", "Idle", 3000);
+
+    var nn = runs.data && runs.data.length>0 && runs.obj['roi']>0 ? runs.obj['run'].reduce(Func.max) : -1;
+    if (nn>0)
+    {
+        var night = runs.obj['night'];
+
+        var yy =  night/10000;
+        var mm = (night/100)%100;
+        var dd =  night%100;
+
+        var filefmt = "/loc_data/raw/%d/%02d/%02d/%8d_%03d.drs.fits";
+
+        dim.log("Trying to restore last DRS calibration #"+nn+"  ["+runs.time+"; "+night+"]");
+
+        // FIXME: Timeout
+        var drs_counter = service_drs.get(0, false).counter;
+        dim.send("FAD_CONTROL/LOAD_DRS_CALIBRATION", filefmt.$(yy, mm, dd, night, nn));
+
+        try
+        {
+            var now = new Date();
+            v8.timeout(3000, function() { if (service_drs.get(0, false).counter>drs_counter) return true; });
+            dim.log("Last DRS calibration restored ["+(new Date()-now)/1000+"s]");
+        }
+        catch (e)
+        {
+            console.warn("Restoring last DRS calibration failed.");
+        }
+    }
+
+    var hist = Hist2D(16, -2048.5, 2048.5, 11, -10, 100);
+
+    var data = event.obj;
+
+    for (var i=0; i<1440; i++)
+        hist.fill(data.avg[i], isNaN(data.rms[i])?-1:data.rms[i]);
+
+    hist.print();
+
+    var stat0 = Func.stat(data.avg, function(val, idx) { if (val<600) console.out(" PIX[hw="+idx+"]="+val); return val<600; });
+    var stat1 = Func.stat(data.rms);
+
+    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, "="));
+
+    //      OK                            UNDERFLOW
+    // ------------------------------------------------------
+    // Avg[min]=722.0                Avg[min]=-380.0
+    // Avg[avg]=815.9 +- 45.9        Avg[avg]= 808.0 +- 102.0
+    // Avg[max]=930.5                Avg[max]= 931.1
+    // Avg[cnt]=0                    Avg[cnt]= 9
+
+    // Rms[min]=14.0                 Rms[min]=13.9
+    // Rms[avg]=16.5 +- 1.6          Rms[avg]=18.8 +- 26.8
+    // Rms[max]=44.0                 Rms[max]=382.1
+
+    if (stat0.count>0)
+    {
+        if (stat0.count>8)
+            throw new Error("Underflow condition detected in about "+parseInt(stat0.count/9+.5)+" DRS.");
+
+        console.warn("There is probably an underflow condition in one DRS... please check manually.");
+    }
+}
+
+service_drs.close();
Index: branches/FACT++_scripts_refactoring/Handler.js
===================================================================
--- branches/FACT++_scripts_refactoring/Handler.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Handler.js	(revision 18221)
@@ -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: branches/FACT++_scripts_refactoring/Hist1D.js
===================================================================
--- branches/FACT++_scripts_refactoring/Hist1D.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Hist1D.js	(revision 18221)
@@ -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: branches/FACT++_scripts_refactoring/Hist2D.js
===================================================================
--- branches/FACT++_scripts_refactoring/Hist2D.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Hist2D.js	(revision 18221)
@@ -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: branches/FACT++_scripts_refactoring/Main.js
===================================================================
--- branches/FACT++_scripts_refactoring/Main.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Main.js	(revision 18221)
@@ -0,0 +1,1527 @@
+/**
+ * @fileOverview This file has functions related to documenting JavaScript.
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+'use strict';
+
+dim.log("Start: "+__FILE__+" ["+__DATE__+"]");
+
+// This should be set in dimctrl.rc as JavaScript.schedule-database.
+// It is sent together with the script to the dimserver.
+// If started directly, it has to be set after the command:
+//
+//   .js scripts/Main.js schedule-database=...
+//
+if (!$['schedule-database'])
+    throw new Error("Environment 'schedule-database' not set!");
+
+//dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
+
+// ================================================================
+//  Code related to the schedule
+// ================================================================
+
+//this is just the class implementation of 'Observation'
+include('scripts/Observation_class.js');
+include('scripts/getSchedule.js');
+
+var observations = [ ];
+
+// Get the observation scheduled for 'now' from the table and
+// return its index
+function getObservation(now)
+{
+    if (now==undefined)
+        now = new Date();
+
+    if (isNaN(now.valueOf()))
+        throw new Error("Date argument in getObservation invalid.");
+
+    observations = getSchedule();
+
+    for (var i=0; i<observations.length; i++)
+        if (now<observations[i].start)
+            return i-1;
+
+    return observations.length-1;
+}
+
+// ================================================================
+//  Code to check whether observation is allowed
+// ================================================================
+/*
+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;
+}
+*/
+
+// ================================================================
+//  Code to perform the DRS calib sequence
+// ================================================================
+
+var irq;
+
+function doDrsCalibration(where)
+{
+    dim.log("Starting DRS calibration ["+where+"]");
+
+    service_feedback.voltageOff();
+
+    var tm = new Date();
+
+    while (!irq)
+    {
+        dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
+        if (irq || !takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        if (irq || !takeRun("drs-gain",     1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        if (irq || !takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        break;
+    }
+
+    dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+    while (!irq && !takeRun("drs-pedestal", 1000));     // 40 / 20s     (50Hz)
+    while (!irq && !takeRun("drs-time",     1000));     // 40 / 20s     (50Hz)
+
+    while (!irq)
+    {
+        dim.send("FAD_CONTROL/RESET_SECONDARY_DRS_BASELINE");
+        if (takeRun("pedestal",     1000))              // 40 / 10s     (80Hz)
+            break;
+    }
+
+    dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+    while (!irq && !takeRun("pedestal",     1000));     // 40 / 10s     (80Hz)
+    //                                                   -----------
+    //                                                   4'40 / 2'00
+
+    if (irq)
+        dim.log("DRS calibration interrupted [%.1fs]".$((new Date()-tm)/1000));
+    else
+        dim.log("DRS calibration done [%.1fs]".$((new Date()-tm)/1000));
+}
+
+// ================================================================
+//  Code related to the lid
+// ================================================================
+
+function OpenLid()
+{
+    /*
+    while (Sun.horizon(-13).isUp)
+    {
+        var now = new Date();
+        var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
+        console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
+        v8.sleep(60000);
+    }*/
+
+    var isClosed = dim.state("LID_CONTROL").name=="Closed";
+
+    var tm = new Date();
+
+    // Wait for lid to be open
+    if (isClosed)
+    {
+        dim.log("Opening lid");
+        dim.send("LID_CONTROL/OPEN");
+    }
+    dim.wait("LID_CONTROL", "Open", 30000);
+
+    if (isClosed)
+        dim.log("Lid open [%.1fs]".$((new Date()-tm)/1000));
+}
+
+function CloseLid()
+{
+    var isOpen = dim.state("LID_CONTROL").name=="Open";
+
+    var tm = new Date();
+
+    // Wait for lid to be open
+    if (isOpen)
+    {
+        if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        {
+            dim.send("FTM_CONTROL/STOP_TRIGGER");
+            dim.wait("FTM_CONTROL", "Valid", 3000);
+        }
+
+        dim.log("Closing lid.");
+        dim.send("LID_CONTROL/CLOSE");
+    }
+    dim.wait("LID_CONTROL", "Closed", 30000);
+
+    if (isOpen)
+        dim.log("Lid closed [%.1fs]".$((new Date()-tm)/1000));
+}
+
+// ================================================================
+//  Interrupt data taking in case of high currents
+// ================================================================
+dim.onchange['FEEDBACK'] = function(state)
+{
+    if ((state.name=="Critical" || state.name=="OnStandby") &&
+        (this.prev!="Critical"  && this.prev!="OnStandby"))
+    {
+        console.out("Feedback state changed from "+this.prev+" to "+state.name+" [Main.js]");
+        irq = "RESCHEDULE";
+    }
+    this.prev=state.name;
+}
+
+// ================================================================
+//  Code related to switching bias voltage on and off
+// ================================================================
+
+var service_feedback = new Subscription("FEEDBACK/CALIBRATED_CURRENTS");
+
+service_feedback.onchange = function(evt)
+{
+    if (!evt.data)
+        return;
+
+    if (this.ok==undefined)
+        return;
+
+    var Unom = evt.obj['U_nom'];
+    var Uov  = evt.obj['U_ov'];
+    if (!Uov)
+        return;
+
+    var cnt = 0;
+    var avg = 0;
+    for (var i=0; i<320; i++)
+    {
+        var dU = Uov[i]-Unom;
+
+        // 0.022 corresponds to 1 DAC count (90V/4096)
+        if (Math.abs(dU)>0.033)
+            cnt++;
+
+        avg += dU;
+    }
+    avg /= 320;
+
+    this.ok = cnt<3;// || (this.last!=undefined && Math.abs(this.last-avg)<0.002);
+
+    console.out("  DeltaUov=%.3f (%.3f) [N(>0.033V)=%d]".$(avg, avg-this.last, cnt));
+
+    this.last = avg;
+}
+
+service_feedback.voltageOff = function()
+{
+    var state = dim.state("BIAS_CONTROL").name;
+
+    if (state=="Disconnected")
+    {
+        console.out("  Voltage off: bias crate disconnected!");
+        return;
+    }
+
+    // check of feedback has to be switched on
+    var isOn = state=="VoltageOn" || state=="Ramping";
+    if (isOn)
+    {
+        dim.log("Switching voltage off.");
+
+        if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        {
+            dim.send("FTM_CONTROL/STOP_TRIGGER");
+            dim.wait("FTM_CONTROL", "Valid", 3000);
+        }
+
+        // Supress the possibility that the bias control is
+        // ramping and will reject the command to switch the
+        // voltage off
+        //dim.send("FEEDBACK/STOP");
+        //dim.wait("FEEDBACK", "Calibrated", 3000);
+
+        // Make sure we are not in Ramping anymore
+        //dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+
+        // Switch voltage off
+        dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+    }
+
+    dim.wait("BIAS_CONTROL", "VoltageOff", 60000); // FIXME: 30000?
+    dim.wait("FEEDBACK",     "Calibrated",  3000);
+
+    // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
+    // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
+
+    if (isOn)
+        dim.log("Voltage off.");
+}
+
+// DN:  The name of the method voltageOn() in the context of the method
+//      voltageOff() is a little bit misleading, since when voltageOff() returns
+//      the caller can be sure the voltage is off, but when voltageOn() return
+//      this is not the case, in the sense, that the caller can now take data.
+//      instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
+//      in order to safely take good-quality data.
+//      This could lead to nasty bugs in the sense, that the second call might 
+//      be forgotten by somebody
+//      
+//      so I suggest to rename voltageOn() --> prepareVoltageOn()
+//      waitForVoltageOn() stays as it is
+//      and one creates a third method called:voltageOn() like this
+/*      service_feedback.voltageOn = function()
+ *      {
+ *          this.prepareVoltageOn();
+ *          this.waitForVoltageOn();
+ *      }
+ * 
+ * */
+//      For convenience.
+
+service_feedback.voltageOn = function(ov)
+{
+    if (isNaN(ov))
+        ov = 1.1;
+
+    if (this.ov!=ov && dim.state("FEEDBACK").name=="InProgress") // FIXME: Warning, OnStandby, Critical if (ov<this.ov)
+    {
+        dim.log("Stoping feedback.");
+        if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        {
+            dim.send("FTM_CONTROL/STOP_TRIGGER");
+            dim.wait("FTM_CONTROL", "Valid", 3000);
+        }
+
+        dim.send("FEEDBACK/STOP");
+        dim.wait("FEEDBACK", "Calibrated", 3000);
+
+        // Make sure we are not in Ramping anymore
+        dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+    }
+
+    var isOff = dim.state("FEEDBACK").name=="Calibrated";
+    if (isOff)
+    {
+        dim.log("Switching voltage to Uov="+ov+"V.");
+
+        dim.send("FEEDBACK/START", ov);
+
+        // FIXME: We could miss "InProgress" if it immediately changes to "Warning"
+        //        Maybe a dim.timeout state>8 ?
+        dim.wait("FEEDBACK", "InProgress", 45000);
+
+        this.ov = ov;
+    }
+
+    // Wait until voltage on
+    dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
+}
+
+service_feedback.waitForVoltageOn = function()
+{
+    // Avoid output if condition is already fulfilled
+    dim.log("Waiting for voltage to be stable.");
+
+    function func()
+    {
+        if (irq || this.ok==true)
+            return true;
+    }
+
+    var now = new Date();
+
+    this.last = undefined;
+    this.ok = false;
+    v8.timeout(4*60000, func, this); // FIMXE: Remove 4!
+    this.ok = undefined;
+
+    if (irq)
+        dim.log("Waiting for stable voltage interrupted.");
+    else
+        dim.log("Voltage stable within limits");
+}
+
+// ================================================================
+//  Function to shutdown the system
+// ================================================================
+
+function Shutdown(type)
+{
+    if (!type)
+        type = "default";
+
+    dim.log("Starting shutdown ["+type+"].");
+
+    var now1 = new Date();
+
+    var bias = dim.state("BIAS_CONTROL").name;
+    if (bias=="VoltageOn" || bias=="Ramping")
+        service_feedback.voltageOn(0);
+
+    CloseLid();
+
+    var now2 = new Date();
+
+    dim.send("DRIVE_CONTROL/PARK");
+
+    console.out("","Waiting for telescope to park. This may take a while.");
+
+    // FIXME: This might not work is the drive is already close to park position
+    dim.wait("DRIVE_CONTROL", "Locked", 3000);
+
+    //unlock drive if task was sleep
+    if (type=="sleep")
+        dim.send("DRIVE_CONTROL/UNLOCK");
+
+    var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
+    sub.get(5000);  // FIXME: Proper error message in case of failure
+
+    function func()
+    {
+        var report = sub.get();
+
+        var zd = report.obj['Zd'];
+        var az = report.obj['Az'];
+
+        if (zd>100 && Math.abs(az)<1)
+            return true;
+
+        return undefined;
+    }
+
+    try { v8.timeout(150000, func); }
+    catch (e)
+    {
+        var p = sub.get();
+        dim.log('Park position not reached? Telescope at Zd='+p.obj['Zd']+' Az='+p.obj['Az']);
+    }
+    /*
+    // Check if DRS calibration is necessary
+    var diff = getTimeSinceLastDrsCalib();
+    if (diff>30 || diff==null)
+    {
+        doDrsCalibration("singlepe");  // will turn voltage off
+        if (irq)
+            break;
+    }*/
+
+    if (type=="singlepe")
+    {
+        dim.log("Taking single-pe run.");
+
+        // The voltage must be on
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        // Before we can switch to 3000 we have to make the right DRS calibration
+        dim.log("Taking single p.e. run.");
+        while (!irq && !takeRun("single-pe", 10000));
+    }
+
+    // It is unclear what comes next, so we better switch off the voltage
+    service_feedback.voltageOff();
+
+    dim.log("Finishing shutdown.");
+
+    var now3 = new Date();
+
+    dim.send("FTM_CONTROL/STOP_TRIGGER");
+    dim.wait("FTM_CONTROL",  "Valid",        3000);
+
+    if (bias!="Disconnected")
+        dim.wait("FEEDBACK", "Calibrated",   3000);
+
+    if (type!="sleep")
+    {
+        dim.send("BIAS_CONTROL/DISCONNECT");
+
+        var pwrctrl_state = dim.state("PWR_CONTROL").name;
+        if (pwrctrl_state=="SystemOn" ||
+            pwrctrl_state=="BiasOff"  ||
+            pwrctrl_state=="DriveOn")
+            dim.send("PWR_CONTROL/TOGGLE_DRIVE");
+
+        dim.wait("BIAS_CONTROL", "Disconnected", 3000);
+        dim.wait("PWR_CONTROL",  "DriveOff",     6000);
+    }
+
+    var report = sub.get();
+
+    console.out("");
+    console.out("Shutdown procedure ["+type+"] seems to be finished...");
+    console.out("  "+new Date().toUTCString());
+    console.out("  Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
+    console.out("  Please check on the web cam that the park position was reached");
+    console.out("  and the telescope is not moving anymore.");
+    console.out("  Please check visually that the lid is really closed and");
+    console.out("  that the biasctrl really switched the voltage off.", "");
+    console.out("    DRIVE_CONTROL: "+dim.state("DRIVE_CONTROL").name);
+    console.out("    FEEDBACK:      "+dim.state("FEEDBACK").name);
+    console.out("    FTM_CONTROL:   "+dim.state("FTM_CONTROL").name);
+    console.out("    BIAS_CONTROL:  "+dim.state("BIAS_CONTROL").name);
+    console.out("    PWR_CONTROL:   "+dim.state("PWR_CONTROL").name);
+    console.out("");
+    dim.log("Shutdown: end ["+(now2-now1)/1000+"s, "+(now3-now2)/1000+"s, "+(new Date()-now3)/1000+"s]");
+    console.out("");
+
+    sub.close();
+}
+
+
+// ================================================================
+//  Function to set the system to sleep-mode
+// ================================================================
+// FIXME: do not repeat code from shutdown-function
+/*
+function GoToSleep()
+{
+    CloseLid();
+
+    var isArmed = dim.state("DRIVE_CONTROL").name=="Armed";
+    if (!isArmed)
+    {
+        dim.log("Drive not ready to move. -> send STOP");
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 5000);
+    }
+
+    dim.send("DRIVE_CONTROL/MOVE_TO 101 0");//park position
+    var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
+    sub.get(5000);  // FIXME: Proper error message in case of failure
+
+    function func()
+    {
+        var report = sub.get();
+
+        var zd = report.obj['Zd'];
+        var az = report.obj['Az'];
+
+        if (zd>100 && Math.abs(az)<1)
+            return true;
+
+        return undefined;
+    }
+
+    try { v8.timeout(150000, func); }
+    catch (e)
+    {
+        var p = sub.get();
+        dim.log('Park position not reached? Telescope at Zd='+p.obj['Zd']+' Az='+p.obj['Az']);
+    }
+    var p2 = sub.get();
+    dim.log('Telescope at Zd=%.1fdeg Az=%.1fdeg'.$(p2.obj['Zd'], p2.obj['Az']));
+    sub.close();
+}
+*/
+
+// ================================================================
+// Check datalogger subscriptions
+// ================================================================
+
+var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS");
+datalogger_subscriptions.get(3000, false);
+
+datalogger_subscriptions.check = function()
+{
+    var obj = this.get();
+    if (!obj.data)
+        throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available.");
+
+    var expected =
+        [
+         "AGILENT_CONTROL_24V/DATA",
+         "AGILENT_CONTROL_50V/DATA",
+         "AGILENT_CONTROL_80V/DATA",
+         "BIAS_CONTROL/CURRENT",
+         "BIAS_CONTROL/DAC",
+         "BIAS_CONTROL/NOMINAL",
+         "BIAS_CONTROL/VOLTAGE",
+         "DRIVE_CONTROL/POINTING_POSITION",
+         "DRIVE_CONTROL/SOURCE_POSITION",
+         "DRIVE_CONTROL/STATUS",
+         "DRIVE_CONTROL/TRACKING_POSITION",
+         "FAD_CONTROL/CONNECTIONS",
+         "FAD_CONTROL/DAC",
+         "FAD_CONTROL/DNA",
+         "FAD_CONTROL/DRS_RUNS",
+         "FAD_CONTROL/EVENTS",
+         "FAD_CONTROL/FEEDBACK_DATA",
+         "FAD_CONTROL/FILE_FORMAT",
+         "FAD_CONTROL/FIRMWARE_VERSION",
+         "FAD_CONTROL/INCOMPLETE",
+         "FAD_CONTROL/PRESCALER",
+         "FAD_CONTROL/REFERENCE_CLOCK",
+         "FAD_CONTROL/REGION_OF_INTEREST",
+         "FAD_CONTROL/RUNS",
+         "FAD_CONTROL/RUN_NUMBER",
+         "FAD_CONTROL/START_RUN",
+         "FAD_CONTROL/STATISTICS1",
+         "FAD_CONTROL/STATS",
+         "FAD_CONTROL/STATUS",
+         "FAD_CONTROL/TEMPERATURE",
+         "FEEDBACK/CALIBRATED_CURRENTS",
+         "FEEDBACK/CALIBRATION",
+         "FEEDBACK/CALIBRATION_R8",
+         "FEEDBACK/CALIBRATION_STEPS",
+/*         "FEEDBACK/REFERENCE",*/
+         "FSC_CONTROL/CURRENT",
+         "FSC_CONTROL/HUMIDITY",
+         "FSC_CONTROL/TEMPERATURE",
+         "FSC_CONTROL/VOLTAGE",
+         "FTM_CONTROL/COUNTER",
+         "FTM_CONTROL/DYNAMIC_DATA",
+         "FTM_CONTROL/ERROR",
+         "FTM_CONTROL/FTU_LIST",
+         "FTM_CONTROL/PASSPORT",
+         "FTM_CONTROL/STATIC_DATA",
+         "FTM_CONTROL/TRIGGER_RATES",
+         "GPS_CONTROL/NEMA",
+         "SQM_CONTROL/DATA",
+         "LID_CONTROL/DATA",
+         "MAGIC_LIDAR/DATA",
+         "MAGIC_WEATHER/DATA",
+         "MCP/CONFIGURATION",
+         "PWR_CONTROL/DATA",
+         "RATE_CONTROL/THRESHOLD",
+         "RATE_SCAN/DATA",
+         "RATE_SCAN/PROCESS_DATA",
+         "TEMPERATURE/DATA",
+         "TIME_CHECK/OFFSET",
+         "TNG_WEATHER/DATA",
+         "TNG_WEATHER/DUST",
+         "PFMINI_CONTROL/DATA",
+        ];
+
+    function map(entry)
+    {
+        if (entry.length==0)
+            return undefined;
+
+        var rc = entry.split(',');
+        if (rc.length!=2)
+            throw new Error("Subscription list entry '"+entry+"' has wrong number of elements.");
+        return rc;
+    }
+
+    var list = obj.data.split('\n').map(map);
+    function check(name)
+    {
+        if (list.every(function(el){return el==undefined || el[0]!=name;}))
+            throw new Error("Subscription to '"+name+"' not available.");
+    }
+
+    expected.forEach(check);
+}
+
+
+
+// ================================================================
+// Crosscheck all states
+// ================================================================
+
+// ----------------------------------------------------------------
+// Do a standard startup to bring the system in into a well
+// defined state
+// ----------------------------------------------------------------
+include('scripts/Startup.js');
+
+// ================================================================
+//  Code to monitor clock conditioner
+// ================================================================
+
+var sub_counter = new Subscription("FTM_CONTROL/COUNTER");
+sub_counter.onchange = function(evt)
+{
+    if (evt.qos>0 && evt.qos!=2 && evt.qos&0x100==0)
+        throw new Error("FTM reports: clock conditioner not locked.");
+}
+
+// ================================================================
+//  Code related to monitoring the fad system
+// ================================================================
+
+// This code is here, because scripts/Startup.js needs the
+// same subscriptions... to be revised.
+var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
+var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
+var sub_startrun = new Subscription("FAD_CONTROL/START_RUN");
+sub_startrun.get(5000);
+
+include('scripts/takeRun.js');
+
+// ----------------------------------------------------------------
+// Check that everything we need is availabel to receive commands
+// (FIXME: Should that go to the general CheckState?)
+// ----------------------------------------------------------------
+//console.out("Checking send.");
+checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
+//console.out("Checking send: done");
+
+// ----------------------------------------------------------------
+// Bring feedback into the correct operational state
+// ----------------------------------------------------------------
+//console.out("Feedback init: start.");
+service_feedback.get(5000);
+
+// ----------------------------------------------------------------
+// Connect to the DRS_RUNS service
+// ----------------------------------------------------------------
+//console.out("Drs runs init: start.");
+
+var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
+sub_drsruns.get(5000);
+// FIXME: Check if the last DRS calibration was complete?
+
+function getTimeSinceLastDrsCalib()
+{
+    // ----- Time since last DRS Calibration [min] ------
+    var runs = sub_drsruns.get(0);
+    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.obj['run'][2]>0 && runs.obj['roi']==300;
+
+    if (valid)
+        dim.log("Last DRS calibration was %.1fmin ago".$(diff));
+    else
+        dim.log("No valid DRS calibration available.");
+
+    return valid ? diff : null;
+}
+
+// ----------------------------------------------------------------
+// Install interrupt handler
+// ----------------------------------------------------------------
+function handleIrq(cmd, args, time, user)
+{
+    console.out("Interrupt received:");
+    console.out("  IRQ:  "+cmd);
+    console.out("  Time: "+time);
+    console.out("  User: "+user);
+
+    irq = cmd ? cmd : "stop";
+
+    // This will end a run in progress as if it where correctly stopped
+    if (dim.state("MCP").name=="TakingData")
+        dim.send("MCP/STOP");
+
+    // This will stop a rate scan in progress
+    if (dim.state("RATE_SCAN").name=="InProgress")
+        dim.send("RATE_SCAN/STOP");
+}
+
+dimctrl.setInterruptHandler(handleIrq);
+
+// ----------------------------------------------------------------
+// Make sure we will write files
+// ----------------------------------------------------------------
+dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+// ----------------------------------------------------------------
+// Print some information for the user about the
+// expected first oberservation
+// ----------------------------------------------------------------
+var test = getObservation();
+if (test!=undefined)
+{
+    var n = new Date();
+    if (observations.length>0 && test==-1)
+        dim.log("First observation scheduled for "+observations[0].start.toUTCString()+" [id="+observations[0].id+"]");
+    if (test>=0 && test<observations.length)
+        dim.log("First observation should start immediately ["+observations[test].start.toUTCString()+", id="+observations[test].id+"]");
+    if (observations.length>0 && observations[0].start>n+12*3600*1000)
+        dim.log("No observations scheduled for the next 12 hours!");
+    if (observations.length==0)
+        dim.log("No observations scheduled!");
+}
+
+// ----------------------------------------------------------------
+// Start main loop
+// ----------------------------------------------------------------
+dim.log("Entering main loop.");
+console.out("");
+
+var run = -2; // getObservation never called
+var sub;
+var lastId;
+var nextId;
+var sun = Sun.horizon(-12);
+var system_on;  // undefined
+
+function processIrq()
+{
+    if (!irq)
+        return false;
+
+    if (irq.toUpperCase()=="RESCHEDULE")
+    {
+        irq = undefined;
+        return false;
+    }
+
+    if (irq.toUpperCase()=="OFF")
+    {
+        service_feedback.voltageOff();
+        dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+        return true;
+    }
+
+    /*
+    if (irq.toUpperCase()=="STOP")
+    {
+        dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+        dim.send("MCP/STOP");
+        return true;
+    }*/
+
+    if (irq.toUpperCase()=="SHUTDOWN")
+    {
+        Shutdown();
+        return true;
+    }
+
+    dim.log("IRQ "+irq+" unhandled... stopping script.");
+    return true;
+}
+
+while (!processIrq())
+{
+    // Check if observation position is still valid
+    // If source position has changed, set run=0
+    var idxObs = getObservation();
+    if (idxObs===undefined)
+        break;
+
+    // we are still waiting for the first observation in the schedule
+    if (idxObs==-1)
+    {
+        // flag that the first observation will be in the future
+        run = -1; 
+        v8.sleep(1000);
+        continue;
+    }
+
+    // Check if we have to take action do to sun-rise
+    var was_up = sun.isUp;
+    sun = Sun.horizon(-12);
+    if (!was_up && sun.isUp)
+    {
+        console.out("");
+        dim.log("Sun rise detected.... automatic shutdown initiated!");
+        // FIXME: State check?
+        Shutdown();
+        system_on = false;
+        continue;
+    }
+
+    // Current and next observation target
+    var obs     = observations[idxObs];
+    var nextObs = observations[idxObs+1];
+
+    // Check if observation target has changed
+    if (lastId!=obs.id) // !Object.isEqual(obs, nextObs)
+    {
+        dim.log("Starting new observation ["+obs.start.toUTCString()+", id="+obs.id+"]");
+
+        // 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
+        sub = 0;
+        if (run<0)
+        {
+            //Startup();   // -> Bias On/Off?, Lid open/closed?
+            //CloseLid();
+        }
+
+        // The first observation had a start-time in the past...
+        // In this particular case start with the last entry
+        // in the list of measurements
+        if (run==-2)
+            sub = obs.length-1;
+
+        run = 0;
+        lastId = obs.id;
+    }
+
+    //dim.log("DEBUG: Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
+    if (nextObs && nextId!=nextObs.id)
+    {
+        dim.log("Next observation scheduled for "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
+        console.out("");
+        nextId = nextObs.id;
+    }
+
+    if (!nextObs && nextId)
+    {
+        dim.log("No further observation scheduled.");
+        console.out("");
+        nextId = undefined;
+    }
+
+    //if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
+    //    throw Error("Last scheduled measurement must be a shutdown.");
+
+    // We are done with all measurement slots for this
+    // observation... wait for next observation
+    if (sub>=obs.length)
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    if (system_on===false && obs[sub].task!="STARTUP")
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    // Check if sun is still up... only DATA and */
+    if ((obs[sub].task=="DATA" || obs[sub].task=="RATESCAN" || obs[sub].task=="RATESCAN2" ) && sun.isUp)
+    {
+        var now = new Date();
+        var remaining = (sun.set - now)/60000;
+        console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
+        v8.sleep(60000);
+        continue;
+    }
+
+
+    if (obs[sub].task!="IDLE" && (obs[sub].task!="DATA" && run>0))
+        dim.log("New task ["+obs[sub]+"]");
+
+    // FIXME: Maybe print a warning if Drive is on during day time!
+
+    // It is not ideal that we allow the drive to be on during day time, but
+    // otherwise it is difficult to allow e.g. the STARTUP at the beginning of the night
+    var power_states = sun.isUp || !system_on ? [ "DriveOff", "SystemOn" ] : [ "SystemOn" ];
+    var drive_states = sun.isUp || !system_on ? undefined : [ "Armed", "Tracking", "OnTrack" ];
+
+    // A scheduled task was found, lets check if all servers are
+    // still only and in reasonable states. If this is not the case,
+    // something unexpected must have happend and the script is aborted.
+    //console.out("  Checking states [general]");
+    var table =
+        [
+         [ "TNG_WEATHER"   ],
+         [ "MAGIC_WEATHER" ],
+         [ "CHAT"          ],
+         [ "SMART_FACT"    ],
+         [ "TEMPERATURE"   ],
+         [ "DATA_LOGGER",         [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
+         [ "FSC_CONTROL",         [ "Connected"                ] ],
+         [ "MCP",                 [ "Idle"                     ] ],
+         [ "TIME_CHECK",          [ "Valid"                    ] ],
+         [ "PWR_CONTROL",         power_states/*[ "SystemOn"                 ]*/ ],
+         [ "AGILENT_CONTROL_24V", [ "VoltageOn"                ] ],
+         [ "AGILENT_CONTROL_50V", [ "VoltageOn"                ] ],
+         [ "AGILENT_CONTROL_80V", [ "VoltageOn"                ] ],
+         [ "BIAS_CONTROL",        [ "VoltageOff", "VoltageOn", "Ramping" ] ],
+         [ "FEEDBACK",            [ "Calibrated", "InProgress", "OnStandby", "Warning", "Critical" ] ],
+         [ "LID_CONTROL",         [ "Open", "Closed"           ] ],
+         [ "DRIVE_CONTROL",       drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
+         [ "FTM_CONTROL",         [ "Valid", "TriggerOn"       ] ],
+         [ "FAD_CONTROL",         [ "Connected", "RunInProgress" ] ],
+         [ "RATE_SCAN",           [ "Connected"                ] ],
+         [ "RATE_CONTROL",        [ "Connected", "GlobalThresholdSet", "InProgress"  ] ],
+         [ "GPS_CONTROL",         [ "Locked"  ] ],
+         [ "SQM_CONTROL",         [ "Disconnected", "Connected", "Valid" ] ],
+         [ "PFMINI_CONTROL",      [ "Disconnected", "Connected", "Receiving" ] ],
+        ];
+
+
+    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...");
+    }
+
+    datalogger_subscriptions.check();                                         
+                                                                                
+    // If this is an observation which needs the voltage to be swicthed on
+    // skip that if the voltage is not stable                                    
+    /*
+    if (obs[sub].task=="DATA" || obs[sub].task=="RATESCAN")
+    {
+        var state = dim.state("FEEDBACK").name;
+        if (state=="Warning" || state=="Critical" || state=="OnStandby")
+        {
+            v8.sleep(1000);
+            continue;
+        }
+    }*/
+
+
+    // Check if obs.task is one of the one-time-tasks
+    switch (obs[sub].task)
+    {
+    case "IDLE":
+        v8.sleep(5000);
+        continue;
+
+    case "SLEEP":
+        Shutdown("sleep"); //GoToSleep();
+
+        sub++;
+        dim.log("Task finished [SLEEP].");
+        console.out("");
+        continue;
+
+    case "STARTUP":
+        CloseLid();
+
+        doDrsCalibration("startup");  // will switch the voltage off
+
+        if (irq)
+            break;
+
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        // Before we can switch to 3000 we have to make the right DRS calibration
+        dim.log("Taking single p.e. run.");
+        while (!irq && !takeRun("single-pe", 10000));
+
+        // It is unclear what comes next, so we better switch off the voltage
+        service_feedback.voltageOff();
+
+        system_on = true;
+        dim.log("Task finished [STARTUP]");
+        console.out("");
+        break;
+
+    case "SHUTDOWN":
+        Shutdown("singlepe");
+        system_on = false;
+
+        // FIXME: Avoid new observations after a shutdown until
+        //        the next startup (set run back to -2?)
+        sub++;
+        dim.log("Task finished [SHUTDOWN]");
+        console.out("");
+        //console.out("  Waiting for next startup.", "");
+        continue;
+
+    case "DRSCALIB":
+        doDrsCalibration("drscalib");  // will switch the voltage off
+        dim.log("Task finished [DRSCALIB]");
+        console.out("");
+        break;
+
+    case "SINGLEPE":
+        // The lid must be closes
+        CloseLid();
+
+        // Check if DRS calibration is necessary
+        var diff = getTimeSinceLastDrsCalib();
+        if (diff>30 || diff==null)
+        {
+            doDrsCalibration("singlepe");  // will turn voltage off
+            if (irq)
+                break;
+        }
+
+        // The voltage must be on
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        // Before we can switch to 3000 we have to make the right DRS calibration
+        dim.log("Taking single p.e. run.");
+        while (!irq && !takeRun("single-pe", 10000));
+
+        // It is unclear what comes next, so we better switch off the voltage
+        service_feedback.voltageOff();
+        dim.log("Task finished [SINGLE-PE]");
+        console.out("");
+        break;
+
+    case "OVTEST":
+        var locked = dim.state("DRIVE_CONTROL").name=="Locked";
+        if (!locked)
+            dim.send("DRIVE_CONTROL/PARK");
+
+        dim.send("FEEDBACK/STOP");
+
+        // The lid must be closed
+        CloseLid();
+
+        if (!locked)
+        {
+            //console.out("Waiting for telescope to park. This may take a while.");
+            dim.wait("DRIVE_CONTROL", "Locked", 3000);
+            dim.send("DRIVE_CONTROL/UNLOCK");
+        }
+
+        // Check if DRS calibration is necessary
+        var diff = getTimeSinceLastDrsCalib();
+        if (diff>30 || diff==null)
+        {
+            doDrsCalibration("ovtest");  // will turn voltage off
+            if (irq)
+                break;
+        }
+
+        // The voltage must be on
+        service_feedback.voltageOn(0.4);
+        service_feedback.waitForVoltageOn();
+
+        dim.log("Taking single p.e. run (0.4V)");
+        while (!irq && !takeRun("single-pe", 10000));
+
+        for (var i=5; i<18 && !irq; i++)
+        {
+            dim.send("FEEDBACK/STOP");
+            dim.wait("FEEDBACK", "Calibrated", 3000);
+            dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+            dim.send("FEEDBACK/START", i*0.1);
+            dim.wait("FEEDBACK", "InProgress", 45000);
+            dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
+            service_feedback.waitForVoltageOn();
+            dim.log("Taking single p.e. run ("+(i*0.1)+"V)");
+            while (!irq && !takeRun("single-pe", 10000));
+        }
+
+        // It is unclear what comes next, so we better switch off the voltage
+        service_feedback.voltageOff();
+        dim.log("Task finished [OVTEST]");
+        console.out("");
+        break;
+
+    case "RATESCAN":
+        var tm1 = new Date();
+
+        // This is a workaround to make sure that we really catch
+        // the new OnTrack state later and not the old one
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 15000);
+
+        // The lid must be open
+        OpenLid();
+
+        // Switch the voltage to a reduced level (Ubd)
+        service_feedback.voltageOn(0);
+
+        if (obs[sub].source != undefined)
+        {
+            dim.log("Pointing telescope to '"+obs[sub].source+"'.");
+            dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
+        }
+        else
+        {
+            dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
+            dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
+        }
+
+        dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+
+        // Now tracking stable, switch voltage to nominal level and wait
+        // for stability.
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        if (!irq)
+        {
+            var tm2 = new Date();
+
+            dim.log("Starting ratescan.");
+
+            //set reference to whole camera (in case it was changed)
+            dim.send("RATE_SCAN/SET_REFERENCE_CAMERA");
+            // Start rate scan
+            dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10, "default");
+
+            // Lets wait if the ratescan really starts... this might take a few
+            // seconds because RATE_SCAN configures the ftm and is waiting for
+            // it to be configured.
+            dim.wait("RATE_SCAN", "InProgress", 10000);
+            dim.wait("RATE_SCAN", "Connected", 2700000);
+
+            // Here one could implement a watchdog for the feedback as well, but what is the difference
+            // whether finally one has to find out if the feedback was in the correct state
+            // or the ratescan was interrupted?
+
+            // this line is actually some kind of hack.
+            // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
+            // So I decided to put this line here as a kind of patchwork....
+            //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+            dim.log("Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
+        }
+
+        dim.log("Task finished [RATESCAN]");
+        console.out("");
+        break; // case "RATESCAN"
+
+    case "RATESCAN2":
+        var tm1 = new Date();
+
+        // This is a workaround to make sure that we really catch
+        // the new OnTrack state later and not the old one
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 15000);
+
+        if (obs[sub].rstype=="dark-bias-off")
+            service_feedback.voltageOff();
+        else
+        {
+            // Switch the voltage to a reduced level (Ubd)
+            var bias = dim.state("BIAS_CONTROL").name;
+            if (bias=="VoltageOn" || bias=="Ramping")
+                service_feedback.voltageOn(0);
+        }
+
+        // Open the lid if required
+        if (!obs[sub].lidclosed)
+            OpenLid();
+        else
+            CloseLid();
+
+        // track source/position or move to position
+        if (obs[sub].lidclosed)
+        {
+            dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az);
+            dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az);
+            v8.sleep(3000);
+            dim.wait("DRIVE_CONTROL", "Armed", 150000); // 110s for turning and 30s for stabilizing
+        }
+        else
+        {
+            if (obs[sub].source != undefined)
+            {
+                dim.log("Pointing telescope to '"+obs[sub].source+"'.");
+                dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
+            }
+            else
+            {
+                dim.log("Pointing telescope to ra="+obs[sub].ra+" dec="+obs[sub].dec);
+                dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
+            }
+
+            dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+        }
+
+        // Now tracking stable, switch voltage to nominal level and wait
+        // for stability.
+        if (obs[sub].rstype!="dark-bias-off")
+        {
+            service_feedback.voltageOn();
+            service_feedback.waitForVoltageOn();
+        }
+
+        if (!irq)
+        {
+            var tm2 = new Date();
+
+            dim.log("Starting ratescan 2/1 ["+obs[sub].rstype+"]");
+
+            //set reference to whole camera (in case it was changed)
+            dim.send("RATE_SCAN/SET_REFERENCE_CAMERA");
+            // Start rate scan
+            dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 300, 20, obs[sub].rstype);
+
+            // Lets wait if the ratescan really starts... this might take a few
+            // seconds because RATE_SCAN configures the ftm and is waiting for
+            // it to be configured.
+            dim.wait("RATE_SCAN", "InProgress", 10000);
+            //FIXME: discuss what best value is here
+            dim.wait("RATE_SCAN", "Connected", 2700000);//45min
+            //dim.wait("RATE_SCAN", "Connected", 1200000);//3.3h
+
+            // Here one could implement a watchdog for the feedback as well, but what is the difference
+            // whether finally one has to find out if the feedback was in the correct state
+            // or the ratescan was interrupted?
+
+            // this line is actually some kind of hack.
+            // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
+            // So I decided to put this line here as a kind of patchwork....
+            //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+            dim.log("Ratescan 2/1 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
+        }
+
+        if (!irq)
+        {
+            var tm2 = new Date();
+
+            dim.log("Starting ratescan 2/2 ["+obs[sub].rstype+"]");
+
+            // Start rate scan
+            dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 300, 1000, 100, obs[sub].rstype);
+
+            // Lets wait if the ratescan really starts... this might take a few
+            // seconds because RATE_SCAN configures the ftm and is waiting for
+            // it to be configured.
+            dim.wait("RATE_SCAN", "InProgress", 10000);
+            dim.wait("RATE_SCAN", "Connected", 2700000);
+
+            // Here one could implement a watchdog for the feedback as well, but what is the difference
+            // whether finally one has to find out if the feedback was in the correct state
+            // or the ratescan was interrupted?
+
+            // this line is actually some kind of hack.
+            // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
+            // So I decided to put this line here as a kind of patchwork....
+            //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 6);
+
+            dim.log("Ratescan 2/2 done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
+        }
+
+        dim.log("Task finished [RATESCAN2]");
+        console.out("");
+        break; // case "RATESCAN2"
+
+    case "CUSTOM":
+
+        // This is a workaround to make sure that we really catch
+        // the new OnTrack state later and not the old one
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 15000);
+
+        // Ramp bias if needed
+        if (!obs[sub].biason)
+            service_feedback.voltageOff();
+        else
+        {
+            // Switch the voltage to a reduced level (Ubd)
+            var bias = dim.state("BIAS_CONTROL").name;
+            if (bias=="VoltageOn" || bias=="Ramping")
+                service_feedback.voltageOn(0);
+        }
+        // Close lid
+        CloseLid();
+
+        // Move to position (zd/az)
+        dim.log("Moving telescope to zd="+obs[sub].zd+" az="+obs[sub].az);
+        dim.send("DRIVE_CONTROL/MOVE_TO", obs[sub].zd, obs[sub].az);
+        v8.sleep(3000);
+        dim.wait("DRIVE_CONTROL", "Armed", 150000); // 110s for turning and 30s for stabilizing
+
+        // Now tracking stable, switch voltage to nominal level and wait
+        // for stability.
+        if (obs[sub].biason)
+        {
+            service_feedback.voltageOn();
+            service_feedback.waitForVoltageOn();
+        }
+
+        if (!irq)
+        {
+            dim.log("Taking custom run with time "+obs[sub].time+"s, threshold="+obs[sub].threshold+", biason="+obs[sub].biason);
+
+            var customRun = function()
+            {
+                v8.sleep(500);//wait that configuration is set
+                dim.wait("FTM_CONTROL", "TriggerOn", 15000);
+                dim.send("FAD_CONTROL/SEND_SINGLE_TRIGGER");
+                dim.send("RATE_CONTROL/STOP");
+                dim.send("FTM_CONTROL/STOP_TRIGGER");
+                dim.wait("FTM_CONTROL", "Valid", 3000);
+                dim.send("FTM_CONTROL/ENABLE_TRIGGER", true);
+                dim.send("FTM_CONTROL/SET_TIME_MARKER_DELAY", 123);
+                dim.send("FTM_CONTROL/SET_THRESHOLD", -1, obs[sub].threshold);
+                v8.sleep(500);//wait that configuration is set
+                dim.send("FTM_CONTROL/START_TRIGGER");
+                dim.wait("FTM_CONTROL", "TriggerOn", 15000);
+            }
+
+            takeRun(customRun, -1, obs[sub].time);
+        }
+        dim.log("Task finished [CUSTOM].");
+        dim.log("");
+        break; // case "CUSTOM"
+
+    case "DATA":
+
+        // ========================== case "DATA" ============================
+    /*
+        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==undefined ? 0 : (nextObs.start-new Date())/60000;
+        //dim.log("DEBUG: remaining: "+remaining+" nextObs="+nextObs+" start="+nextObs.start);
+
+        // ------------------------------------------------------------
+
+        dim.log("Run count "+run+" [remaining "+parseInt(remaining)+"min]");
+
+        // ----- Time since last DRS Calibration [min] ------
+        var diff = getTimeSinceLastDrsCalib();
+
+        // 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)) || diff==null;
+    
+        if (point)
+        {
+            // Switch the voltage to a reduced voltage level
+            service_feedback.voltageOn(0);
+
+            // Change wobble position every four runs,
+            // start with alternating wobble positions each day
+            var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
+
+            //console.out("  Move telescope to '"+source+"' "+offset+" "+wobble);
+            dim.log("Pointing telescope to '"+obs[sub].source+"' [wobble="+wobble+"]");
+
+            // This is a workaround to make sure that we really catch
+            // the new OnTrack state later and not the old one
+            dim.send("DRIVE_CONTROL/STOP");
+            dim.wait("DRIVE_CONTROL", "Armed", 15000);
+
+            dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, obs[sub].source);
+
+            // Do we have to check if the telescope is really moving?
+            // We can cross-check the SOURCE service later
+        }
+
+        if (drscal)
+        {
+            doDrsCalibration("data");  // will turn voltage off
+
+            // Now we switch on the voltage and a significant amount of
+            // time has been passed, so do the check again.
+            sun = Sun.horizon(-12);
+            if (!was_up && sun.isUp)
+            {
+                dim.log("Sun rise detected....");
+                continue;
+            }
+        }
+
+        if (irq)
+            continue;
+
+        OpenLid();
+
+        // 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 check the voltage... (do not start a lot of stuff just to do nothing)
+        var state = dim.state("FEEDBACK").name;
+        if (state=="Warning" || state=="Critical" || state=="OnStandby")
+        {
+            v8.sleep(60000);
+            continue;
+        }
+
+        // Now we are 'OnTrack', so we can ramp to nominal voltage
+        // and wait for the feedback to get stable
+        service_feedback.voltageOn();
+        service_feedback.waitForVoltageOn();
+
+        // If pointing had changed, do calibration
+        if (!irq && point)
+        {
+            dim.log("Starting calibration.");
+
+            // Calibration (2% of 20')
+            while (!irq)
+            {
+                if (irq || !takeRun("pedestal",         1000))  // 80 Hz  -> 10s
+                    continue;
+                if (irq || !takeRun("light-pulser-ext", 1000))  // 80 Hz  -> 10s
+                    continue;
+                break;
+            }
+        }
+
+        //console.out("  Taking data: start [5min]");
+
+        // FIXME: What do we do if during calibration something has happened
+        // e.g. drive went to ERROR? Maybe we have to check all states again?
+
+        var twilight = Sun.horizon(-16).isUp;
+
+        if (twilight)
+        {
+            for (var i=0; i<5 && !irq; i++)
+                takeRun("data", -1, 60); // Take data (1min)
+        }
+        else
+        {
+            var len = 300;
+            while (!irq && len>15)
+            {
+                var time = new Date();
+                if (takeRun("data", -1, len)) // Take data (5min)
+                    break;
+
+                len -= parseInt((new Date()-time)/1000);
+            }
+        }
+
+        //console.out("  Taking data: done");
+        run++;
+
+        continue; // case "DATA"
+    }
+
+    if (nextObs!=undefined && sub==obs.length-1)
+        dim.log("Next observation will start at "+nextObs.start.toUTCString()+" [id="+nextObs.id+"]");
+
+    sub++;
+}
+
+sub_drsruns.close();
+
+dim.log("Left main loop [irq="+irq+"]");
+
+// ================================================================
+// Comments and ToDo goes here
+// ================================================================
+
+// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
+// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
Index: branches/FACT++_scripts_refactoring/MainClassic.js
===================================================================
--- branches/FACT++_scripts_refactoring/MainClassic.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/MainClassic.js	(revision 18221)
@@ -0,0 +1,1263 @@
+/**
+ * @fileOverview This file has functions related to documenting JavaScript.
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+'use strict';
+
+dim.log("Start: "+__FILE__+" ["+__DATE__+"]");
+
+//dimctrl.defineState(37, "TimeOutBeforeTakingData", "MCP took more than 5minutes to start TakingData");
+
+// ================================================================
+//  Code related to the schedule
+// ================================================================
+
+//this is just the class implementation of 'Observation'
+include('scripts/Observation_class.js');
+
+// this file just contains the definition of
+// the variable observations, which builds our nightly schedule, hence the filename
+include('scripts/schedule.js');
+
+// make Observation objects from user input and check if 'date' is increasing.
+for (var i=0; i<observations.length; i++)
+{
+    observations[i] = new Observation(observations[i]);
+
+    // check if the start date given by the user is increasing.
+    if (i>0 && observations[i].start <= observations[i-1].start)
+    {
+        throw new Error("Start time '"+ observations[i].start.toUTCString()+
+                        "' in row "+i+" exceeds start time in row "+(i-1));
+    }
+}
+
+// Get the observation scheduled for 'now' from the table and
+// return 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;
+}
+
+// ================================================================
+//  Code to check whether observation is allowed
+// ================================================================
+/*
+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;
+}
+*/
+
+// ----------------------------------------------------------------
+
+// ================================================================
+//  Code related to monitoring the fad system
+// ================================================================
+
+var sub_incomplete = new Subscription("FAD_CONTROL/INCOMPLETE");
+
+var incomplete = 0;
+
+sub_incomplete.onchange = function(evt)
+{
+    if (!evt.data)
+        return;
+
+    var inc = evt.obj['incomplete'];
+    if (!inc || inc>0xffffffffff)
+        return;
+
+    if (incomplete>0)
+        return;
+
+    if (dim.state("MCP").name!="TakingData")
+        return;
+
+    incomplete = inc;
+
+    console.out("Sending MCP/STOP");
+    dim.send("MCP/STOP");
+}
+
+var sub_connections = new Subscription("FAD_CONTROL/CONNECTIONS");
+
+/**
+ * call-back function of FAD_CONTROL/CONNECTIONS
+ * store IDs of problematic FADs 
+ *
+ */
+/*
+sub_connections.onchange = function(evt)
+{
+    // This happens, but why?
+    if (!evt.obj['status'])
+        return;
+
+    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;
+
+    //m.alarm("FAD board loss detected...");
+    dim.send("MCP/RESET");
+    dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+}
+*/
+
+/**
+ * reconnect to problematic FADs
+ *
+ * Dis- and Reconnects to FADs, found to be problematic by call-back function
+ * onchange() to have a different CONNECTION value than 66 or 67. 
+ * 
+ * @returns
+ *      a boolean is returned. 
+ *      reconnect returns true if:
+ *          * nothing needed to be reset --> no problems found by onchange()
+ *          * the reconnection went fine.
+ *      
+ *      reconnect *never returns false* so far.
+ *
+ * @example
+ *      if (!sub_connections.reconnect())
+ *          exit();
+ */
+sub_connections.reconnect = function()
+{
+    // this.reset is a list containing the IDs of FADs, 
+    // which have neither CONNECTION==66 nor ==67, whatever this means :-)
+    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;
+}
+
+// ================================================================
+//  Code related to taking data
+// ================================================================
+
+var startrun = new Subscription("FAD_CONTROL/START_RUN");
+startrun.get(5000);
+
+function reconnect(list, txt)
+{ /*
+    var reset = [ ];
+
+    for (var i=0; i<list.length; i++)
+        {
+            console.out("  FAD %2d".$(list[i])+" lost during "+txt);
+            reset.push(parseInt(list[i]/10));
+        }
+
+    reset = reset.filter(function(elem,pos){return reset.indexOf(elem)==pos;});
+
+    console.out("");
+    console.out("  FADs belong to crate(s): "+reset);
+    console.out("");
+*/
+    console.out("");
+    console.out("Trying automatic reconnect ["+txt+"]...");
+
+    for (var i=0; i<list.length; i++)
+    {
+        console.out("   ...disconnect "+list[i]);
+        dim.send("FAD_CONTROL/DISCONNECT", list[i]);
+    }
+
+    console.out("   ...waiting for 5s");
+    v8.sleep(5000);
+
+    for (var i=0; i<list.length; i++)
+    {
+        console.out("   ...reconnect "+list[i]);
+        dim.send("FAD_CONTROL/CONNECT", list[i]);
+    }
+
+    console.out("   ...waiting for 1s");
+    v8.sleep(1000);
+    console.out("");
+}
+
+function takeRun(type, count, time)
+{
+    if (!count)
+        count = -1;
+    if (!time)
+        time = -1;
+
+    var nextrun = startrun.get().obj['next'];
+    console.out("  Take run %3d".$(nextrun)+": N="+count+" T="+time+"s ["+type+"]");
+
+    incomplete = 0;
+    dim.send("MCP/START", time?time:-1, count?count:-1, type);
+
+    // FIXME: Replace by callback?
+    //
+    // DN: I believe instead of waiting for 'TakingData' one could split this
+    // up into two checks with an extra condition:
+    //  if type == 'data':
+    //      wait until ThresholdCalibration starts:
+    //          --> this time should be pretty identical for each run
+    //      if this takes longer than say 3s:
+    //          there might be a problem with one/more FADs
+    //    
+    //      wait until "TakingData":
+    //          --> this seems to take even some minutes sometimes... 
+    //              (might be optimized rather soon, but still in the moment...)
+    //      if this takes way too long: 
+    //          there might be something broken, 
+    //          so maybe a very high time limit is ok here.
+    //          I think there is not much that can go wrong, 
+    //          when the Thr-Calib has already started. Still it might be nice 
+    //          If in the future RateControl is written so to find out that 
+    //          in case the threshold finding algorithm does 
+    //          *not converge as usual*
+    //          it can complain, and in this way give a hint, that the weather
+    //          might be a little bit too bad.
+    //  else:
+    //      wait until "TakingData":
+    //          --> in a non-data run this time should be pretty short again
+    //      if this takes longer than say 3s:
+    //          there might be a problem with one/more FADs
+    //  
+
+    // Use this if you use the rate control to calibrate by rates
+    //if (!dim.wait("MCP", "TakingData", -300000) )
+    //{
+    //    throw new Error("MCP took longer than 5 minutes to start TakingData"+
+    //                    "maybe this idicates a problem with one of the FADs?");
+    //}
+
+    // Here we could check and handle fad losses
+
+    try
+    {
+        dim.wait("MCP", "TakingData", 15000);
+    }
+    catch (e)
+    {
+        console.out("");
+        console.out("MCP:         "+dim.state("MCP").name);
+        console.out("FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
+        console.out("FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
+        console.out("");
+
+        if (dim.state("MCP").name!="Configuring3" ||
+            dim.state("FAD_CONTROL").name!="Configuring2")
+            throw e;
+
+        console.out("");
+        console.out("Waiting for fadctrl to get configured timed out... checking for in-run FAD loss.");
+
+        var con  = sub_connections.get();
+        var stat = con.obj['status'];
+
+        console.out("Sending MCP/RESET");
+        dim.send("MCP/RESET");
+
+        dim.wait("FTM_CONTROL", "Idle",      3000);
+        dim.wait("FAD_CONTROL", "Connected", 3000);
+        dim.wait("MCP",         "Idle",      3000);
+
+        /*** FOR REMOVE ***/
+        var reset = [ ];
+
+        for (var i=0; i<40; i++)
+            if (stat[i]!=0x43)
+            {
+                console.out("  FAD %2d".$(i)+" not in Configured state.");
+                reset.push(parseInt(i/10));
+            }
+
+        reset = reset.filter(function(elem,pos){return reset.indexOf(elem)==pos;});
+
+        if (reset.length>0)
+        {
+            console.out("");
+            console.out("  FADs belong to crate(s): "+reset);
+            console.out("");
+        }
+        /**** FOR REMOVE ****/
+
+        var list = [];
+        for (var i=0; i<40; i++)
+            if (stat[i]!=0x43)
+                list.push(i);
+
+        reconnect(list, "configuration");
+
+        throw e;
+    }
+
+    dim.wait("MCP", "Idle", time>0 ? time*1250 : undefined); // run time plus 25%
+
+    if (incomplete)
+    {
+        console.out("Incomplete: "+incomplete);
+
+        console.out("");
+        console.out("MCP:         "+dim.state("MCP").name);
+        console.out("FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
+        console.out("FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
+        console.out("");
+
+        dim.wait("MCP",         "Idle", 3000);
+        dim.wait("FTM_CONTROL", "Idle", 3000);
+
+        // Necessary to allow the disconnect, reconnect
+        dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+        dim.wait("FAD_CONTROL", "Connected", 3000);
+
+        var list = [];
+        for (var i=0; i<40; i++)
+            if (incomplete&(1<<i))
+                list.push(i);
+
+        reconnect(list, "data taking");
+
+        throw new Error("In-run FAD loss detected.");
+    }
+
+    //console.out("  Take run: end");
+
+    // DN: currently reconnect() never returns false 
+    //     .. but it can fail of course.
+    //if (!sub_connections.reconnect())
+    //    exit();
+
+    return true;//sub_connections.reconnect();
+}
+
+// ----------------------------------------------------------------
+
+function doDrsCalibration(where)
+{
+    console.out("  Take DRS calibration ["+where+"]");
+
+    service_feedback.voltageOff();
+
+    var tm = new Date();
+
+    while (1)
+    {
+        dim.send("FAD_CONTROL/START_DRS_CALIBRATION");
+        if (!takeRun("drs-pedestal", 1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        // Does that fix the runopen before runclose problem?
+        //dim.wait("FAD_CONTROL", "Connected", 3000);
+        //v8.sleep(1000);
+
+        if (!takeRun("drs-gain",     1000))     // 40 / 20s     (50Hz)
+            continue;
+
+        // Does that fix the runopen before runclose problem?
+        //dim.wait("FAD_CONTROL", "Connected", 3000);
+        //v8.sleep(1000);
+
+        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 calibration done [%.1f]".$((new Date()-tm)/1000));
+}
+
+// ================================================================
+//  Code related to the lid
+// ================================================================
+
+function OpenLid()
+{
+    /*
+    while (Sun.horizon(-13).isUp)
+    {
+        var now = new Date();
+        var minutes_until_sunset = (Sun.horizon(-13).set - now)/60000;
+        console.out(now.toUTCString()+": Sun above FACT-horizon, lid cannot be opened: sleeping 1min, remaining %.1fmin".$(minutes_until_sunset));
+        v8.sleep(60000);
+    }*/
+
+    var isClosed = dim.state("LID_CONTROL").name=="Closed";
+
+    var tm = new Date();
+
+    // Wait for lid to be open
+    if (isClosed)
+    {
+        console.out("  Open lid: start");
+        dim.send("LID_CONTROL/OPEN");
+    }
+    dim.wait("LID_CONTROL", "Open", 30000);
+
+    if (isClosed)
+        console.out("  Open lid: done [%.1fs]".$((new Date()-tm)/1000));
+}
+
+function CloseLid()
+{
+    var isOpen = dim.state("LID_CONTROL").name=="Open";
+
+    var tm = new Date();
+
+    // Wait for lid to be open
+    if (isOpen)
+    {
+        console.out("  Close lid: start");
+        dim.send("LID_CONTROL/CLOSE");
+    }
+    dim.wait("LID_CONTROL", "Closed", 30000);
+
+    if (isOpen)
+        console.out("  Close lid: end [%.1fs]".$((new Date()-tm)/1000));
+}
+
+// ================================================================
+//  Code related to switching bias voltage on and off
+// ================================================================
+
+var service_feedback = new Subscription("FEEDBACK/DEVIATION");
+
+service_feedback.onchange = function(evt)
+{
+    if (this.cnt && evt.counter>this.cnt+12)
+        return;
+
+    this.voltageStep = null;
+    if (!evt.data)
+        return;
+
+    var delta = evt.obj['DeltaBias'];
+
+    var avg = 0;
+    for (var i=0; i<320; i++)
+        avg += delta[i];
+    avg /= 320;
+
+    if (this.previous)
+        this.voltageStep = Math.abs(avg-this.previous);
+
+    this.previous = avg;
+
+    console.out("  DeltaV="+this.voltageStep);
+}
+
+// DN:  Why is voltageOff() implemented as 
+//      a method of a Subscription to a specific Service
+//      I naively would think of voltageOff() as an unbound function.
+//      I seems to me it has to be a method of a Subscription object, in order
+//      to use the update counting method. But does it have to be
+//      a Subscription to FEEDBACK/DEVIATION, or could it work with other services as well?
+service_feedback.voltageOff = function()
+{
+    var state = dim.state("BIAS_CONTROL").name;
+
+    // check of feedback has to be switched on
+    var isOn = state=="VoltageOn" || state=="Ramping";
+    if (isOn)
+    {
+        console.out("  Voltage off: start");
+
+        // Supress the possibility that the bias control is
+        // ramping and will reject the command to switch the
+        // voltage off
+        var isControl = dim.state("FEEDBACK").name=="CurrentControl";
+        if (isControl)
+        {
+            console.out("  Suspending feedback.");
+            dim.send("FEEDBACK/ENABLE_OUTPUT", false);
+            dim.wait("FEEDBACK", "CurrentCtrlIdle", 3000);
+        }
+
+        // Switch voltage off
+        console.out("  Voltage on: switch off");
+        dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+
+        // If the feedback was enabled, re-enable it
+        if (isControl)
+        {
+            console.out("  Resuming feedback.");
+            dim.send("FEEDBACK/ENABLE_OUTPUT", true);
+            dim.wait("FEEDBACK", "CurrentControl", 3000);
+        }
+    }
+
+    dim.wait("BIAS_CONTROL", "VoltageOff", 5000);
+
+    // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
+    // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
+
+    if (isOn)
+        console.out("  Voltage off: end");
+}
+
+// DN:  The name of the method voltageOn() in the context of the method
+//      voltageOff() is a little bit misleading, since when voltageOff() returns
+//      the caller can be sure the voltage is off, but when voltageOn() return
+//      this is not the case, in the sense, that the caller can now take data.
+//      instead the caller of voltageOn() *must* call waitForVoltageOn() afterwards
+//      in order to safely take good-quality data.
+//      This could lead to nasty bugs in the sense, that the second call might 
+//      be forgotten by somebody
+//      
+//      so I suggest to rename voltageOn() --> prepareVoltageOn()
+//      waitForVoltageOn() stays as it is
+//      and one creates a third method called:voltageOn() like this
+/*      service_feedback.voltageOn = function()
+ *      {
+ *          this.prepareVoltageOn();
+ *          this.waitForVoltageOn();
+ *      }
+ * 
+ * */
+//      For convenience.
+
+service_feedback.voltageOn = function()
+{
+    //if (Sun.horizon("FACT").isUp)
+    //    throw new Error("Sun is above FACT-horizon, voltage cannot be switched on.");
+
+    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)
+    {
+        console.out("  Voltage on: cnt="+this.cnt);
+
+        this.previous = undefined;
+        this.cnt = this.get().counter;
+        this.voltageStep = undefined;
+    }
+}
+
+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
+
+    // Avoid output if condition is already fulfilled
+    if (this.cnt && this.get().counter>this.cnt+10)
+        return;
+
+    // FIXME: timeout missing
+    console.out("  Feedback wait: start");
+
+    function func(service)
+    {
+        if ((service.cnt!=undefined && service.get().counter>service.cnt+10) ||
+            (service.voltageStep && service.voltageStep<0.02))
+            return true;
+    }
+
+    var now = new Date();
+    //v8.timeout(5*60000, func, this);
+    while ((this.cnt==undefined || this.get().counter<=this.cnt+10) && (!this.voltageStep || this.voltageStep>0.02))
+        v8.sleep();
+
+    console.out("  Feedback wait: end [dV=%.3f, cnt=%d, %.2fs]".$(this.voltageStep, this.get().counter, (new Date()-now)/1000));
+}
+
+// ================================================================
+//  Function to shutdown the system
+// ================================================================
+
+function Shutdown()
+{
+    console.out("Shutdown: start");
+
+    service_feedback.voltageOff();
+    CloseLid(); 
+    dim.send("DRIVE_CONTROL/PARK");
+
+    console.out("Waiting for telescope to park. This may take a while.");
+
+    // FIXME: This might not work is the drive is already close to park position
+    dim.wait("DRIVE_CONTROL", "Locked", 3000);
+
+    var sub = new Subscription("DRIVE_CONTROL/POINTING_POSITION");
+    sub.get(5000);  // FIXME: Proper error message in case of failure
+
+    function func()
+    {
+        var report = sub.get();
+
+        var zd = report.obj['Zd'];
+        var az = report.obj['Az'];
+
+        if (zd>100 && Math.abs(az)<1)
+            return true;
+
+        return undefined;
+    }
+
+    var now = new Date();
+    v8.timeout(150000, func);
+
+    //dim.send("FEEDBACK/STOP");
+    dim.send("FEEDBACK/ENABLE_OUTPUT", false);
+    dim.send("FTM_CONTROL/STOP_TRIGGER");
+
+    dim.wait("FEEDBACK", "CurrentCtrlIdle", 3000);
+    dim.wait("FTM_CONTROL", "Idle", 3000);
+
+    var report = sub.get();
+
+    console.out("");
+    console.out("Shutdown procedure seems to be finished...");
+    console.out("  Telescope at Zd=%.1fdeg Az=%.1fdeg".$(report.obj['Zd'], report.obj['Az']));
+    console.out("  Please make sure the park position was reached");
+    console.out("  and the telescope is not moving anymore.");
+    console.out("  Please check that the lid is closed and the voltage switched off.");
+    console.out("");
+    console.out("Shutdown: end ["+(new Date()-now)/1000+"s]");
+
+    sub.close();
+}
+
+// ================================================================
+// Check datalogger subscriptions
+// ================================================================
+
+var datalogger_subscriptions = new Subscription("DATA_LOGGER/SUBSCRIPTIONS");
+datalogger_subscriptions.get(3000, false);
+
+datalogger_subscriptions.check = function()
+{
+    var obj = this.get();
+    if (!obj.data)
+        throw new Error("DATA_LOGGER/SUBSCRIPTIONS not available.");
+
+    var expected =
+        [
+         "BIAS_CONTROL/CURRENT",
+         "BIAS_CONTROL/DAC",
+         "BIAS_CONTROL/NOMINAL",
+         "BIAS_CONTROL/VOLTAGE",
+         "DRIVE_CONTROL/POINTING_POSITION",
+         "DRIVE_CONTROL/SOURCE_POSITION",
+         "DRIVE_CONTROL/STATUS",
+         "DRIVE_CONTROL/TRACKING_POSITION",
+         "FAD_CONTROL/CONNECTIONS",
+         "FAD_CONTROL/DAC",
+         "FAD_CONTROL/DNA",
+         "FAD_CONTROL/DRS_RUNS",
+         "FAD_CONTROL/EVENTS",
+         "FAD_CONTROL/FEEDBACK_DATA",
+         "FAD_CONTROL/FILE_FORMAT",
+         "FAD_CONTROL/FIRMWARE_VERSION",
+         "FAD_CONTROL/INCOMPLETE",
+         "FAD_CONTROL/PRESCALER",
+         "FAD_CONTROL/REFERENCE_CLOCK",
+         "FAD_CONTROL/REGION_OF_INTEREST",
+         "FAD_CONTROL/RUNS",
+         "FAD_CONTROL/RUN_NUMBER",
+         "FAD_CONTROL/START_RUN",
+         "FAD_CONTROL/STATISTICS1",
+         "FAD_CONTROL/STATISTICS2",
+         "FAD_CONTROL/STATS",
+         "FAD_CONTROL/STATUS",
+         "FAD_CONTROL/TEMPERATURE",
+         "FEEDBACK/CALIBRATED_CURRENTS",
+         "FEEDBACK/CALIBRATION",
+         "FEEDBACK/DEVIATION",
+         "FEEDBACK/REFERENCE",
+         "FSC_CONTROL/CURRENT",
+         "FSC_CONTROL/HUMIDITY",
+         "FSC_CONTROL/TEMPERATURE",
+         "FSC_CONTROL/VOLTAGE",
+         "FTM_CONTROL/COUNTER",
+         "FTM_CONTROL/DYNAMIC_DATA",
+         "FTM_CONTROL/ERROR",
+         "FTM_CONTROL/FTU_LIST",
+         "FTM_CONTROL/PASSPORT",
+         "FTM_CONTROL/STATIC_DATA",
+         "FTM_CONTROL/TRIGGER_RATES",
+         "LID_CONTROL/DATA",
+         "MAGIC_LIDAR/DATA",
+         "MAGIC_WEATHER/DATA",
+         "MCP/CONFIGURATION",
+         "PWR_CONTROL/DATA",
+         "RATE_CONTROL/THRESHOLD",
+         "RATE_SCAN/DATA",
+         "RATE_SCAN/PROCESS_DATA",
+         "TEMPERATURE/DATA",
+         "TIME_CHECK/OFFSET",
+         "TNG_WEATHER/DATA",
+         "TNG_WEATHER/DUST",
+        ];
+
+    function map(entry)
+    {
+        if (entry.length==0)
+            return undefined;
+
+        var rc = entry.split(',');
+        if (rc.length!=2)
+            throw new Error("Subscription list entry '"+entry+"' has wrong number of elements.");
+        return rc;
+    }
+
+    var list = obj.data.split('\n').map(map);
+
+    function check(name)
+    {
+        if (list.every(function(el){return el[0]!=name;}))
+            throw new Error("Subscription to '"+name+"' not available.");
+    }
+
+    expected.forEach(check);
+}
+
+
+
+// ================================================================
+// Crosscheck all states
+// ================================================================
+
+// ----------------------------------------------------------------
+// Do a standard startup to bring the system in into a well
+// defined state
+// ----------------------------------------------------------------
+include('scripts/Startup.js');
+
+// ----------------------------------------------------------------
+// Check that everything we need is availabel to receive commands
+// (FIXME: Should that go to the general CheckState?)
+// ----------------------------------------------------------------
+console.out("Checking send.");
+checkSend(["MCP", "DRIVE_CONTROL", "LID_CONTROL", "FAD_CONTROL", "FEEDBACK"]);
+console.out("Checking send: done");
+
+// ----------------------------------------------------------------
+// Bring feedback into the correct operational state
+// ----------------------------------------------------------------
+console.out("Feedback init: start.");
+service_feedback.get(5000);
+
+dim.send("FEEDBACK/ENABLE_OUTPUT", true);
+dim.send("FEEDBACK/START_CURRENT_CONTROL", 0.);
+
+v8.timeout(3000, function() { var n = dim.state("FEEDBACK").name; if (n=="CurrentCtrlIdle" || n=="CurrentControl") return true; });
+
+// ----------------------------------------------------------------
+// Connect to the DRS_RUNS service
+// ----------------------------------------------------------------
+console.out("Drs runs init: start.");
+
+var sub_drsruns = new Subscription("FAD_CONTROL/DRS_RUNS");
+sub_drsruns.get(5000);
+// FIXME: Check if the last DRS calibration was complete?
+
+function getTimeSinceLastDrsCalib()
+{
+    // ----- Time since last DRS Calibration [min] ------
+    var runs = sub_drsruns.get(0);
+    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.obj['run'][2]>0 && runs.obj['roi']==300;
+
+    if (valid)
+        console.out("  Last DRS calib: %.1fmin ago".$(diff));
+    else
+        console.out("  No valid drs calibration available");
+
+    return valid ? diff : null;
+}
+
+// ----------------------------------------------------------------
+// Make sure we will write files
+// ----------------------------------------------------------------
+dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
+
+// ----------------------------------------------------------------
+// Print some information for the user about the
+// expected first oberservation
+// ----------------------------------------------------------------
+var test = getObservation();
+if (test!=undefined)
+{
+    var n = new Date();
+    if (test==-1)
+        console.out(n.toUTCString()+": First observation scheduled for "+observations[0].start.toUTCString());
+    if (test>=0 && test<observations.length)
+        console.out(n.toUTCString()+": First observation should start immediately.");
+    if (observations[0].start>n+12*3600*1000)
+        console.out(n.toUTCString()+": No observations scheduled for the next 12 hours!");
+}
+
+// ----------------------------------------------------------------
+// Start main loop
+// ----------------------------------------------------------------
+console.out("Start main loop.");
+
+var run = -2; // getObservation never called
+var sub;
+var lastObs;
+var sun = Sun.horizon(-13);
+var system_on;  // undefined
+
+while (1)
+{
+    // Check if observation position is still valid
+    // If source position has changed, set run=0
+    var idxObs = getObservation();
+    if (idxObs===undefined)
+        break;
+
+    // we are still waiting for the first observation in the schedule
+    if (idxObs==-1)
+    {
+        // flag that the first observation will be in the future
+        run = -1; 
+        v8.sleep(1000);
+        continue;
+    }
+
+    // Check if we have to take action do to sun-rise
+    var was_up = sun.isUp;
+    sun = Sun.horizon(-13);
+    if (!was_up && sun.isUp)
+    {
+        console.out("", "Sun rise detected.... automatic shutdown initiated!");
+        // FIXME: State check?
+        Shutdown();
+        system_on = false;
+        continue;
+    }
+
+    // Current and next observation target
+    var obs     = observations[idxObs];
+    var nextObs = observations[idxObs+1];
+
+    // Check if observation target has changed
+    if (lastObs!=idxObs) // !Object.isEqual(obs, nextObs)
+    {
+        console.out("--- "+idxObs+" ---");
+        console.out("Current time:        "+new Date().toUTCString());
+        console.out("Current observation: "+obs.start.toUTCString());
+        if (nextObs!=undefined)
+            console.out("Next    observation: "+nextObs.start.toUTCString());
+        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
+        sub = 0;
+        if (run<0)
+        {
+            //Startup();   // -> Bias On/Off?, Lid open/closed?
+            //CloseLid();
+        }
+
+        // The first observation had a start-time in the past...
+        // In this particular case start with the last entry
+        // in the list of measurements
+        if (run==-2)
+            sub = obs.length-1;
+
+        run = 0;
+    }
+    lastObs = idxObs;
+
+    if (nextObs==undefined && obs[obs.length-1].task!="SHUTDOWN")
+        throw Error("Last scheduled measurement must be a shutdown.");
+
+    // We are done with all measurement slots for this
+    // observation... wait for next observation
+    if (sub>=obs.length)
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    var task = obs[sub].task;
+
+    if (system_on===false && task!="STARTUP")
+    {
+        v8.sleep(1000);
+        continue;
+    }
+
+    // Check if sun is still up... only DATA and RATESCAN must be suppressed
+    if ((task=="DATA" || task=="RATESCAN") && sun.isUp)
+    {
+        var now = new Date();
+        var remaining = (sun.set - now)/60000;
+        console.out(now.toUTCString()+" - "+obs[sub].task+": Sun above FACT-horizon: sleeping 1min, remaining %.1fmin".$(remaining));
+        v8.sleep(60000);
+        continue;
+    }
+
+    console.out("\n"+(new Date()).toUTCString()+": Current measurement: "+obs[sub]);
+
+    var power_states = sun.isUp || system_on===false ? [ "DriveOff" ] : [ "SystemOn" ];
+    var drive_states = sun.isUp || system_on===false ?   undefined    : [ "Armed", "Tracking", "OnTrack" ];
+
+    // A scheduled task was found, lets check if all servers are
+    // still only and in reasonable states. If this is not the case,
+    // something unexpected must have happend and the script is aborted.
+    //console.out("  Checking states [general]");
+    var table =
+        [
+         [ "TNG_WEATHER"   ],
+         [ "MAGIC_WEATHER" ],
+         [ "CHAT"          ],
+         [ "SMART_FACT"    ],
+         [ "TEMPERATURE"   ],
+         [ "DATA_LOGGER",     [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
+         [ "FSC_CONTROL",     [ "Connected"                ] ],
+         [ "MCP",             [ "Idle"                     ] ],
+         [ "TIME_CHECK",      [ "Valid"                    ] ],
+         [ "PWR_CONTROL",     power_states/*[ "SystemOn"                 ]*/ ],
+//         [ "AGILENT_CONTROL", [ "VoltageOn"                ] ],
+         [ "BIAS_CONTROL",    [ "VoltageOff", "VoltageOn", "Ramping" ] ],
+         [ "FEEDBACK",        [ "CurrentControl", "CurrentCtrlIdle" ] ],
+         [ "LID_CONTROL",     [ "Open", "Closed"           ] ],
+         [ "DRIVE_CONTROL",   drive_states/*[ "Armed", "Tracking", "OnTrack" ]*/ ],
+         [ "FTM_CONTROL",     [ "Idle", "TriggerOn"        ] ],
+         [ "FAD_CONTROL",     [ "Connected", "WritingData" ] ],
+         [ "RATE_SCAN",       [ "Connected"                ] ],
+         [ "RATE_CONTROL",    [ "Connected", "GlobalThresholdSet", "InProgress"  ] ],
+        ];
+
+    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...");
+    }
+
+    datalogger_subscriptions.check();
+
+    // Check if obs.task is one of the one-time-tasks
+    switch (obs[sub].task)
+    {
+    case "STARTUP":
+        console.out("  STARTUP", "");
+        CloseLid();
+
+        doDrsCalibration("startup");  // will switch the voltage off
+
+        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));
+
+        // It is unclear what comes next, so we better switch off the voltage
+        service_feedback.voltageOff();
+        system_on = true;
+        break;
+
+    case "SHUTDOWN":
+        console.out("  SHUTDOWN", "");
+        Shutdown();
+        system_on = false;
+
+        // FIXME: Avoid new observations after a shutdown until
+        //        the next startup (set run back to -2?)
+        console.out("  Waiting for next startup.", "");
+        sub++;
+        continue;
+
+    case "IDLE":
+        v8.sleep(1000);
+        continue;
+
+    case "DRSCALIB":
+        console.out("  DRSCALIB", "");
+        doDrsCalibration("drscalib");  // will switch the voltage off
+        break;
+
+    case "SINGLEPE":
+        console.out("  SINGLE-PE", "");
+
+        // The lid must be closes
+        CloseLid();
+
+        // Check if DRS calibration is necessary
+        var diff = getTimeSinceLastDrsCalib();
+        if (diff>30 || diff==null)
+            doDrsCalibration("singlepe");  // will turn voltage off
+
+        // The voltage must be on
+        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));
+
+        // It is unclear what comes next, so we better switch off the voltage
+        service_feedback.voltageOff();
+        break;
+
+    case "RATESCAN":
+        console.out("  RATESCAN", "");
+
+        var tm1 = new Date();
+
+        // This is a workaround to make sure that we really catch
+        // the new state and not the old one
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 5000);
+
+        // The lid must be open
+        OpenLid();
+
+        // The voltage must be switched on
+        service_feedback.voltageOn();
+
+        if (obs.source != undefined)
+            dim.send("DRIVE_CONTROL/TRACK_ON", obs[sub].source);
+        else
+            dim.send("DRIVE_CONTROL/TRACK", obs[sub].ra, obs[sub].dec);
+
+        dim.wait("DRIVE_CONTROL", "OnTrack", 150000); // 110s for turning and 30s for stabilizing
+
+        service_feedback.waitForVoltageOn();
+
+        var tm2 = new Date();
+
+        // Start rate scan
+        dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 50, 1000, -10);
+
+        // Lets wait if the ratescan really starts... this might take a few
+        // seconds because RATE_SCAN configures the ftm and is waiting for
+        // it to be configured.
+        dim.wait("RATE_SCAN", "InProgress", 10000);
+        dim.wait("RATE_SCAN", "Connected", 2700000);
+
+        // this line is actually some kind of hack. 
+        // after the Ratescan, no data is written to disk. I don't know why, but it happens all the time
+        // So I decided to put this line here as a kind of patchwork....
+        //dim.send("FAD_CONTROL/SET_FILE_FORMAT", 2);
+
+        console.out("  Ratescan done [%.1fs, %.1fs]".$((tm2-tm1)/1000, (new Date()-tm2)/1000));
+        break; // case "RATESCAN"
+
+    case "DATA":
+
+        // ========================== case "DATA" ============================
+    /*
+        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==undefined ? 0 : (nextObs.start-new Date())/60000;
+
+        // ------------------------------------------------------------
+
+        console.out("  Run #"+run+"  (remaining "+parseInt(remaining)+"min)");
+
+        // ----- Time since last DRS Calibration [min] ------
+        var diff = getTimeSinceLastDrsCalib();
+
+        // 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)) || diff==null;
+
+        if (point)
+        {
+            // Change wobble position every four runs,
+            // start with alternating wobble positions each day
+            var wobble = (parseInt(run/4) + parseInt(new Date()/1000/3600/24-0.5))%2+1;
+
+            //console.out("  Move telescope to '"+source+"' "+offset+" "+wobble);
+            console.out("  Move telescope to '"+obs[sub].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, obs[sub].source);
+
+            // Do we have to check if the telescope is really moving?
+            // We can cross-check the SOURCE service later
+        }
+
+        if (drscal)
+            doDrsCalibration("data");  // will turn voltage off
+
+        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)
+                break;
+
+            len -= parseInt((new Date()-time)/1000);
+        }
+
+        console.out("  Taking data: done");
+        run++;
+
+        continue; // case "DATA"
+    }
+
+    if (nextObs!=undefined && sub==obs.length-1)
+        console.out("  Waiting for next observation scheduled for "+nextObs.start.toUTCString(),"");
+
+    sub++;
+}
+
+sub_drsruns.close();
+
+// ================================================================
+// Comments and ToDo goes here
+// ================================================================
+
+// error handline : http://www.sitepoint.com/exceptional-exception-handling-in-javascript/
+// classes: http://www.phpied.com/3-ways-to-define-a-javascript-class/
+//
+// Arguments: TakeFirstDrsCalib
+// To be determined: How to stop the script without foreceful interruption?
Index: branches/FACT++_scripts_refactoring/Observation_class.js
===================================================================
--- branches/FACT++_scripts_refactoring/Observation_class.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Observation_class.js	(revision 18221)
@@ -0,0 +1,124 @@
+'use strict';
+
+//
+// this file contains just the implementation of the
+// Observation class (I know there are no classes in javascript...)
+//
+
+function Observation(obj)
+{
+    if (typeof(obj)!='object')
+        throw new Error("Observation object can only be constructed using an object.");
+
+    if (!obj.date)
+        throw new Error("Observation object must have a 'date' parameter");
+
+    var ret = [];
+
+    // FIXME: Check transisiton from summer- and winter-time!!
+    var utc = obj.date.toString().toUpperCase()=="NOW" ? new Date() : new Date(obj.date);
+    if (isNaN(utc.valueOf()))
+        throw new Error('"'+obj.date+'" not a valid Date... try something like "2013-01-08 23:05 UTC".');
+    ret.start = utc;
+    ret.id    = obj.id;
+
+    // If the given data is not an array, make it the first entry of an array
+    // so that we can simply loop over all entries
+    if (obj.measurements.length===undefined)
+    {
+        var cpy = obj.measurements;
+        obj.measurements = [];
+        obj.measurements[0] = cpy;
+    }
+
+    for (var i=0; i<obj.measurements.length; i++)
+    {
+        var obs = obj.measurements[i];
+
+        ret[i] = { };
+        ret[i].task   = obs.task ? obs.task.toUpperCase() : "DATA";
+        ret[i].source = obs.source;
+        ret[i].ra     = parseFloat(obs.ra);
+        ret[i].dec    = parseFloat(obs.dec);
+        ret[i].zd     = parseFloat(obs.zd);
+        ret[i].az     = parseFloat(obs.az);
+        ret[i].time   = parseInt(obs.time);
+        ret[i].threshold = parseInt(obs.threshold);
+        ret[i].lidclosed = obs.lidclosed;
+        ret[i].biason = obs.biason;
+        ret[i].rstype = obs.rstype ? obs.rstype : "default";
+        ret[i].sub    = i;
+        ret[i].start  = utc;
+
+        ret[i].toString = function()
+        {
+            var rc = this.task;
+            rc += "["+this.sub+"]";
+            if (this.source)
+                rc += ": " + this.source;
+            //rc += " ["+this.start.toUTCString()+"]";
+            return rc;
+        }
+
+        switch (ret[i].task)
+        {
+        case 'DATA':
+            if (i!=obj.measurements.length-1)
+                throw new Error("DATA [n="+i+", "+utc.toUTCString()+"] must be the last in the list of measurements [cnt="+obj.measurements.length+"]");
+            if (ret[i].source == undefined)
+                throw new Error("Observation must have either 'source' or 'task' " +
+                                "if 'task' == 'data' it must have also have 'source' ");
+            if (ret[i].lidclosed == true)
+                throw new Error("Observation must not have 'lidclosed'== true " +
+                                "if 'task' == 'data' ");
+            break;
+
+        case 'STARTUP':
+            if (ret[i].source != undefined)
+                console.out("warning. Observation with task='startup' also has source defined");
+            break;
+
+        case 'SHUTDOWN':
+            if (ret[i].source != undefined)
+                console.out("warning. Observation with task='shutdown' also has source defined");
+            break;
+
+        case 'RATESCAN':
+            if (ret[i].source == undefined && (isNaN(ret[i].ra) || isNaN(ret[i].dec)))
+                throw new Error("Observation must have either 'source' or 'ra' & 'dec' " +
+                                "if 'task' == 'ratescan'");
+            if (ret[i].lidclosed == true)
+                throw new Error("Observation must not have 'lidclosed'== true " +
+                                "if 'task' == 'ratescan' ");
+            break;
+
+        case 'RATESCAN2':
+            if ((ret[i].lidclosed != true) && ret[i].source == undefined && (isNaN(ret[i].ra) || isNaN(ret[i].dec)))
+                throw new Error("Observation must have either 'source' or 'ra' & 'dec' " +
+                                "if 'task' == 'ratescan2' and lidclosed==false or not given");
+            if (ret[i].lidclosed == true && (isNaN(ret[i].az) || isNaN(ret[i].az)))
+                throw new Error("Observation must have 'zd' & 'az' " +
+                                "if 'task' == 'ratescan2' and option 'lidclosed'=='true'");
+            break;
+
+        case 'CUSTOM':
+
+            if (isNaN(ret[i].az) || isNaN(ret[i].az) || isNaN(ret[i].time) || isNaN(ret[i].threshold))
+                throw new Error("Observation must have 'zd' & 'az', 'time' and 'threshold' " +
+                                "if 'task' == 'custom' ");
+            break;
+
+        case 'SINGLEPE':
+        case 'OVTEST':
+        case 'DRSCALIB':
+        case 'IDLE':
+        case 'SLEEP':
+            break;
+
+        default:
+            throw new Error("The observation type "+ret[i].task+" is unknown.");
+        }
+    }
+
+    return ret;
+}
Index: branches/FACT++_scripts_refactoring/Startup.js
===================================================================
--- branches/FACT++_scripts_refactoring/Startup.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/Startup.js	(revision 18221)
@@ -0,0 +1,332 @@
+'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("");
+dim.alarm();
+
+var loop;
+include("scripts/Handler.js");
+include("scripts/CheckStates.js");
+
+// -----------------------------------------------------------------
+// Make sure camera electronics is switched on and has power
+// -----------------------------------------------------------------
+
+include("scripts/handleAgilentPowerOn24V.js");
+include("scripts/handleAgilentPowerOn50V.js");
+include("scripts/handleAgilentPowerOn80V.js");
+include("scripts/handlePwrCameraOn.js");
+
+checkSend(["AGILENT_CONTROL_24V","AGILENT_CONTROL_50V","AGILENT_CONTROL_80V","PWR_CONTROL"]);
+
+loop = new Handler("PowerOn");
+//loop.add(handleAgilentPowerOn24V);
+//loop.add(handleAgilentPowerOn50V);
+//loop.add(handleAgilentPowerOn80V);
+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");
+include("scripts/handleFadConnected.js");
+
+checkSend(["BIAS_CONTROL","FAD_CONTROL","FTM_CONTROL", "FSC_CONTROL", "FEEDBACK", "RATE_CONTROL", "MCP"]);
+
+dim.send("MCP/RESET");
+
+loop = new Handler("SystemSetup");
+loop.add(handleBiasVoltageOff);
+loop.add(handleFtmIdle);
+loop.add(handleFscConnected);
+loop.add(handleFadConnected);
+loop.add(handleFeedbackConnected); // Feedback needs FAD to be Connected
+loop.add(handleRatectrlConnected);
+loop.add(handleLidClosed);
+loop.run();
+
+console.out("biasctrl:    "+dim.state("BIAS_CONTROL").name);
+console.out("ftmctrl:     "+dim.state("FTM_CONTROL").name);
+console.out("fscctrl:     "+dim.state("FSC_CONTROL").name);
+console.out("feedback:    "+dim.state("FEEDBACK").name);
+console.out("ratecontrol: "+dim.state("RATE_CONTROL").name);
+console.out("fadctrl:     "+dim.state("FAD_CONTROL").name);
+console.out("mcp:         "+dim.state("MCP").name);
+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.log("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 check the clock conditioner
+// -----------------------------------------------------------------
+
+var sub_counter = new Subscription("FTM_CONTROL/COUNTER");
+var counter = sub_counter.get(3000, false).counter;
+dim.send("FTM_CONTROL/REQUEST_STATIC_DATA");
+v8.timeout(3000, function() { if (sub_counter.get(0, false).counter>counter) return true; });
+if (sub_counter.get(0, false).qos&0x100==0)
+    throw new Error("Clock conditioner not locked.");
+sub_counter.close();
+
+// -----------------------------------------------------------------
+// Now we can safely try to connect the FAD boards.
+// -----------------------------------------------------------------
+/*
+ include("scripts/handleFadConnected.js");
+
+// If FADs already connected
+
+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 cabinet not powered... Switching on.");
+    dim.send("PWR_CONTROL/TOGGLE_DRIVE");
+    v8.timeout(5000, function() { if (dim.state("PWR_CONTROL").index&16) return true; });
+}
+
+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
+function makeCurrentCalibration()
+{
+    dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+    dim.wait("BIAS_CONTROL", "VoltageOff", 30000); // waS: 15000
+
+    var now = new Date();
+    dim.send("FEEDBACK/CALIBRATE");
+
+    console.out("Wait for calibration to start");
+    dim.wait("FEEDBACK", "Calibrating", 5000);
+
+    console.out("Wait for calibration to end");
+    dim.wait("FEEDBACK", "Calibrated", 90000);
+
+    console.out("Calibration finished ["+(new Date()-now)+"ms]");
+
+    console.out("Wait for voltage to be off");
+    dim.wait("BIAS_CONTROL", "VoltageOff", 30000); // was: 15000
+}
+
+// Check age of calibration
+var service_calibration = new Subscription("FEEDBACK/CALIBRATION");
+
+var data_calibration = service_calibration.get(3000, false);
+
+var age = data_calibration.time;
+var now = new Date();
+
+var diff = (now-age)/3600000;
+
+var fb_state = dim.state("FEEDBACK").index;
+
+// !data_calibration.data: FEEDBACK might just be freshly
+// started and will not yet serve this service.
+if (fb_state<5 || (diff>8 && now.getHours()>16))
+{
+    if (fb_state<5)
+        console.out("No BIAS crate calibration available: New calibration needed.");
+    else
+        console.out("Last BIAS crate calibration taken at "+age.toUTCString()+": New calibration needed.");
+
+    makeCurrentCalibration();
+}
+
+service_calibration.close();
+
+// ================================================================
+// Setup GPS control and wait for the satellites to be locked
+// ================================================================
+
+checkSend(["GPS_CONTROL"]);
+
+if (dim.state("GPS_CONTROL").name=="Disconnected")
+    dim.send("GPS_CONTROL/RECONNECT");
+
+// Wait for being connectes
+v8.timeout(5000, function() { if (dim.state("GPS_CONTROL").name!="Disconnected") return true; });
+
+// Wait for status available
+v8.timeout(5000, function() { if (dim.state("GPS_CONTROL").name!="Connected") return true; });
+
+if (dim.state("GPS_CONTROL").name=="Disabled")
+    dim.send("GPS_CONTROL/ENABLE");
+
+// Wait for gps to be enabled and locked
+dim.wait("GPS_CONTROL", "Locked", 15000);
+
+// ================================================================
+// Crosscheck all states
+// ================================================================
+
+// FIXME: Check if there is a startup scheduled, if not do not force
+// drive to be switched on
+
+var table =
+[
+ [ "TNG_WEATHER"   ],
+ [ "MAGIC_WEATHER" ],
+ [ "CHAT"          ],
+ [ "SMART_FACT"    ],
+ [ "TEMPERATURE"   ],
+ [ "EVENT_SERVER",        [ "Running", "Standby" ] ],
+ [ "DATA_LOGGER",         [ "NightlyFileOpen", "WaitForRun", "Logging" ] ],
+ [ "FSC_CONTROL",         [ "Connected"                       ] ],
+ [ "MCP",                 [ "Idle"                            ] ],
+ [ "TIME_CHECK",          [ "Valid"                           ] ],
+ [ "PWR_CONTROL",         [ "SystemOn"                        ] ],
+ [ "AGILENT_CONTROL_24V", [ "VoltageOn"                       ] ],
+ [ "AGILENT_CONTROL_50V", [ "VoltageOn"                       ] ],
+ [ "AGILENT_CONTROL_80V", [ "VoltageOn"                       ] ],
+ [ "BIAS_CONTROL",        [ "VoltageOff"                      ] ],
+ [ "FEEDBACK",            [ "Calibrated"                      ] ],
+ [ "RATE_SCAN",           [ "Connected"                       ] ],
+ [ "RATE_CONTROL",        [ "Connected"                       ] ],
+ [ "DRIVE_CONTROL",       [ "Armed", "Tracking", "OnTrack", "Locked" ] ],
+ [ "LID_CONTROL",         [ "Open", "Closed"                  ] ],
+ [ "FTM_CONTROL",         [ "Valid", "TriggerOn"              ] ],
+ [ "FAD_CONTROL",         [ "Connected", "WritingData"        ] ],
+ [ "GPS_CONTROL",         [ "Locked" ] ],
+ [ "SQM_CONTROL",         [ "Valid" ] ],
+ [ "PFMINI_CONTROL",      [ "Receiving" ] ],
+];
+
+
+
+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: branches/FACT++_scripts_refactoring/closed_lid_ratescan.js
===================================================================
--- branches/FACT++_scripts_refactoring/closed_lid_ratescan.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/closed_lid_ratescan.js	(revision 18221)
@@ -0,0 +1,143 @@
+voltageOff = function()
+{
+    var state = dim.state("BIAS_CONTROL").name;
+
+    if (state=="Disconnected")
+    {
+        console.out("  Voltage off: bias crate disconnected!");
+        return;
+    }
+
+    // check of feedback has to be switched on
+    var isOn = state=="VoltageOn" || state=="Ramping";
+    if (isOn)
+    {
+        dim.log("Switching voltage off.");
+
+        if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        {
+            dim.send("FTM_CONTROL/STOP_TRIGGER");
+            dim.wait("FTM_CONTROL", "Valid", 3000);
+        }
+
+        // Supress the possibility that the bias control is
+        // ramping and will reject the command to switch the
+        // voltage off
+        //dim.send("FEEDBACK/STOP");
+        //dim.wait("FEEDBACK", "Calibrated", 3000);
+
+        // Make sure we are not in Ramping anymore
+        //dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+
+        // Switch voltage off
+        dim.send("BIAS_CONTROL/SET_ZERO_VOLTAGE");
+    }
+
+    dim.wait("BIAS_CONTROL", "VoltageOff", 60000); // FIXME: 30000?
+    dim.wait("FEEDBACK",     "Calibrated",  3000);
+
+    // FEEDBACK stays in CurrentCtrl when Voltage is off but output enabled
+    // dim.wait("FEEDBACK", "CurrentCtrlIdle", 1000);
+
+    if (isOn)
+        dim.log("Voltage off.");
+}
+
+waitForVoltageOn = function()
+{
+    // Avoid output if condition is already fulfilled
+    dim.log("Waiting for voltage to be stable.");
+
+    function func()
+    {
+        if (this.ok==true)
+            return true;
+    }
+
+    var now = new Date();
+
+    this.last = undefined;
+    this.ok = false;
+    v8.timeout(4*60000, func, this); // FIMXE: Remove 4!
+    this.ok = undefined;
+
+    dim.log("Voltage On(?)");
+
+    //if (irq)
+        //dim.log("Waiting for stable voltage interrupted.");
+    //else
+        //dim.log("Voltage stable within limits");
+}
+
+
+voltageOn = function(ov)
+{
+    if (isNaN(ov))
+        ov = 1.1;
+
+    if (this.ov!=ov && dim.state("FEEDBACK").name=="InProgress") // FIXME: Warning, OnStandby, Critical if (ov<this.ov)
+    {
+        dim.log("Stoping feedback.");
+        if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        {
+            dim.send("FTM_CONTROL/STOP_TRIGGER");
+            dim.wait("FTM_CONTROL", "Valid", 3000);
+        }
+
+        dim.send("FEEDBACK/STOP");
+        dim.wait("FEEDBACK", "Calibrated", 3000);
+
+        // Make sure we are not in Ramping anymore
+        dim.wait("BIAS_CONTROL", "VoltageOn", 3000);
+    }
+
+    var isOff = dim.state("FEEDBACK").name=="Calibrated";
+    if (isOff)
+    {
+        dim.log("Switching voltage to Uov="+ov+"V.");
+
+        dim.send("FEEDBACK/START", ov);
+
+        // FIXME: We could miss "InProgress" if it immediately changes to "Warning"
+        //        Maybe a dim.timeout state>8 ?
+        dim.wait("FEEDBACK", "InProgress", 45000);
+
+        this.ov = ov;
+    }
+
+    // Wait until voltage on
+    dim.wait("BIAS_CONTROL", "VoltageOn", 60000); // FIXME: 30000?
+}
+
+ dim.log("STARTING SCRIPT closed_lid_ratescan");
+
+// dim.log("Sending Voltage On");
+// voltageOn();
+ v8.sleep(10000);
+
+     var tm = new Date();
+
+     dim.log("Starting ratescan.");
+
+     // Start rate scan
+     //dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 200, 900, 20);
+     dim.send("RATE_SCAN/START_THRESHOLD_SCAN", 100, 900, 10);
+
+      // PAUSE
+     dim.log("PAUSE 10s");
+     v8.sleep(10000);
+     dim.log("Start Ratescan");
+
+     // Lets wait if the ratescan really starts... this might take a few
+     dim.wait("RATE_SCAN", "InProgress", 10000);
+     //dim.wait("RATE_SCAN", "Connected", 2700000);
+     dim.wait("RATE_SCAN", "Connected", 3500000);
+
+     dim.log("Ratescan done");
+
+
+// voltageOff();
+
+ dim.log("Task finished [RATESCAN]");
+ console.out("");
+
Index: branches/FACT++_scripts_refactoring/crateReset.js
===================================================================
--- branches/FACT++_scripts_refactoring/crateReset.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/crateReset.js	(revision 18221)
@@ -0,0 +1,159 @@
+'use strict';
+
+// ==========================================================================
+// Reset Crate
+// ==========================================================================
+
+// call it with: .js doCrateReset.js crate0=true crate3=true
+//           or: DIM_CONTROL/START doCrateReset.js crate0=true crate3=true
+
+// -------------------------------------------------------------------------
+
+include('scripts/CheckStates.js');
+
+function crateReset(crate)
+{
+    var msg = "Starting crate reset:";
+
+    var cnt = 0;
+    for (var i=0; i<4; i++)
+        if (crate[i])
+        {
+            cnt++;
+            msg += " "+i;
+        }
+
+    if (cnt==0)
+    {
+        console.out("No crate to reset.");
+        return;
+    }
+
+    dim.log(msg);
+
+    console.out("Checking availability of servers...");
+
+    var table =
+        [
+         [ "MCP" ],
+         [ "FTM_CONTROL" ],
+         [ "FAD_CONTROL" ],
+        ];
+    if (!checkStates(table, 3000))
+        throw new Error("Either MCP, FTM_CONTROL or FAD_CONTROL not online.");
+
+    // No data taking should be in progress
+    // Trigger must be switched off
+    checkSend(["MCP", "FAD_CONTROL", "FTM_CONTROL" ]);
+
+
+    var mcp = dim.state("MCP");
+    if (mcp.name=="TriggerOn" || mcp.state=="TakingData")
+        dim.send("MCP/STOP");
+    if (mcp.name.substr(0,7)=="kConfig")
+        dim.send("MCP/RESET");
+    if (dim.state("FTM_CONTROL").name=="TriggerOn")
+        dim.send("FTM_CONTROL/STOP_TRIGGER");
+    if (dim.state("FTM_CONTROL").name.indexOf("Config")==0)
+        dim.send("FTM_CONTROL/RESET_CONFIGURE");
+
+    console.out("Checking status of servers...");
+
+    var table =
+        [
+         [ "MCP", [ "Idle", "Connected" ]],
+         [ "FTM_CONTROL", [ "Valid" ] ],
+         [ "FAD_CONTROL", [ "Disengaged", "Disconnected", "Connecting", "Connected" ] ],
+        ];
+    if (!checkStates(table, 3000, true))
+        throw new Error("Either MCP, FTM_CONTROL or FAD_CONTROL not in a state in which it ought to be.");
+
+    // FTUs must be switched off
+
+    console.out("Disable FTUs...");
+
+    //checkSend(["FTM_CONTROL"]);
+    dim.send("FTM_CONTROL/ENABLE_FTU", -1, false);
+    v8.sleep(1000);
+
+    // Boards in the crates must be disconnected
+
+    //checkSend(["FAD_CONTROL"]);
+
+    dim.log("Disconnecting crates.");
+
+    if (dim.state("FAD_CONTROL").name=="Connecting" || dim.state("FAD_CONTROL").name=="Connected")
+        for (var i=0; i<10; i++)
+        {
+            for (var j=0; j<4; j++)
+                if (crate[j])
+                {
+                    console.out("Sending DISCONNECT "+(j*10+i));
+                    dim.send("FAD_CONTROL/DISCONNECT", j*10+i);
+                }
+        }
+
+    v8.sleep(2000);
+    if (!checkStates([[ "FAD_CONTROL", [ "Disengaged", "Disconnected", "Connected" ] ]]))
+        throw new Error("FAD_CONTROL neither Disengaged, Disconnected not Connected.");
+
+
+    // Reset crates
+
+    dim.log("Sending reset.");
+
+    if (cnt==4)
+        dim.send("FTM_CONTROL/RESET_CAMERA");
+    else
+    {
+        for (var i=0; i<4; i++)
+            if (crate[i])
+            {
+                console.out("Sending RESET_CRATE "+i);
+                dim.send("FTM_CONTROL/RESET_CRATE", i);
+            }
+    }
+
+    // We have to wait a bit
+
+    v8.sleep(3200);
+
+    // Reconnect all boards
+
+    dim.log("Waiting for connection.");
+
+    if (dim.state("FAD_CONTROL").name=="Disengaged")
+    {
+        dim.send("Waiting 38s for crates to finish reset.");
+        v8.sleep(38000);
+        dim.send("FAD_CONTROL", "START");
+    }
+    else
+        for (var i=0; i<10; i++)
+        {
+            v8.sleep(3200);
+            for (var j=0; j<4; j++)
+                if (crate[j])
+                {
+                    console.out("Sending CONNECT "+(j*10+i));
+                    dim.send("FAD_CONTROL/CONNECT", j*10+i);
+                }
+        }
+
+
+    // Reconnect all FTUs
+
+    console.out("Enable FTUs...");
+
+    v8.sleep(1000);
+    dim.send("FTM_CONTROL/ENABLE_FTU", -1, true);
+    v8.sleep(3000);
+    dim.send("FTM_CONTROL/PING");
+    v8.sleep(1000);
+
+    dim.wait("FAD_CONTROL", "Connected", 3000);
+
+    // Done
+
+    dim.log("Crate reset finished.");
+}
Index: branches/FACT++_scripts_refactoring/doCrateReset.js
===================================================================
--- branches/FACT++_scripts_refactoring/doCrateReset.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doCrateReset.js	(revision 18221)
@@ -0,0 +1,24 @@
+'use strict';
+
+// ==========================================================================
+// Reset Crate
+// ==========================================================================
+
+// call it with: .js doCrateReset.js crate0=true crate3=true
+//           or: DIM_CONTROL/START doCrateReset.js crate0=true crate3=true
+
+// -------------------------------------------------------------------------
+
+include('scripts/CheckStates.js');
+
+var crates =
+[
+     $['camera']=='true' || $['crate0']=='true',
+     $['camera']=='true' || $['crate1']=='true',
+     $['camera']=='true' || $['crate2']=='true',
+     $['camera']=='true' || $['crate3']=='true'
+];
+
+include('scripts/crateReset.js');
+
+crateReset(crates);
Index: branches/FACT++_scripts_refactoring/doDrivePark.js
===================================================================
--- branches/FACT++_scripts_refactoring/doDrivePark.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doDrivePark.js	(revision 18221)
@@ -0,0 +1,6 @@
+console.out("Sending drive park...");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/PARK");
Index: branches/FACT++_scripts_refactoring/doDriveStop.js
===================================================================
--- branches/FACT++_scripts_refactoring/doDriveStop.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doDriveStop.js	(revision 18221)
@@ -0,0 +1,6 @@
+console.out("Sending drive stop.");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/STOP");
Index: branches/FACT++_scripts_refactoring/doDriveToggle.js
===================================================================
--- branches/FACT++_scripts_refactoring/doDriveToggle.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doDriveToggle.js	(revision 18221)
@@ -0,0 +1,6 @@
+console.out("Sending troggle drive.");
+
+include("scripts/CheckStates.js");
+
+checkSend(["PWR_CONTROL"]);
+dim.send("PWR_CONTROL/TOGGLE_DRIVE");
Index: branches/FACT++_scripts_refactoring/doDriveUnlock.js
===================================================================
--- branches/FACT++_scripts_refactoring/doDriveUnlock.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doDriveUnlock.js	(revision 18221)
@@ -0,0 +1,6 @@
+console.out("Sending drive unlock...");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/UNLOCK");
Index: branches/FACT++_scripts_refactoring/doMoveTelescope.js
===================================================================
--- branches/FACT++_scripts_refactoring/doMoveTelescope.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doMoveTelescope.js	(revision 18221)
@@ -0,0 +1,15 @@
+var zd = $['zd'];
+var az = $['az'];
+
+if (isNaN(zd) || zd<-100 || zd>100)
+    throw new Error("Invalid zenith distance!");
+
+if (isNaN(az) || az<-290 || az>80)
+    throw new Error("Invalid azimuth!");
+
+console.out("Moving telescope to zd="+zd+"deg, az="+az+"deg");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/MOVE_TO", zd, az);
Index: branches/FACT++_scripts_refactoring/doTrackPosition.js
===================================================================
--- branches/FACT++_scripts_refactoring/doTrackPosition.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doTrackPosition.js	(revision 18221)
@@ -0,0 +1,9 @@
+var ra = $['ra'];
+var dec = $['dec'];
+
+console.out("Start tracking at ra="+ra+"h, dec="+dec+"deg");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/TRACK", ra, dec);
Index: branches/FACT++_scripts_refactoring/doTrackSource.js
===================================================================
--- branches/FACT++_scripts_refactoring/doTrackSource.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doTrackSource.js	(revision 18221)
@@ -0,0 +1,16 @@
+var offset = parseFloat($['offset']);
+var angle  = parseFloat($['wobble']);
+var source = $['source'];
+
+if (isNaN(offset) || offset<0)
+    throw new Error("Invalid wobble offset!");
+
+if (isNaN(angle) || angle<0 || angle>360)
+    throw new Error("Invalid wobble angle!");
+
+console.out("Start tracking source "+source+" at wobble angle "+angle+"deg and offset "+offset+"deg");
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/TRACK_SOURCE", offset, angle, source);
Index: branches/FACT++_scripts_refactoring/doTrackWobble.js
===================================================================
--- branches/FACT++_scripts_refactoring/doTrackWobble.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doTrackWobble.js	(revision 18221)
@@ -0,0 +1,9 @@
+var wobble = $['wobble']=='true' ? 2 : 1;
+var source = $['source'];
+
+console.out("Start tracking wobble position "+wobble+" of "+source);
+
+include("scripts/CheckStates.js");
+
+checkSend(["DRIVE_CONTROL"]);
+dim.send("DRIVE_CONTROL/TRACK_WOBBLE", wobble, source);
Index: branches/FACT++_scripts_refactoring/doc/Database.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Database.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Database.js	(revision 18221)
@@ -0,0 +1,97 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of Database connection object
+ */
+
+/**
+ * @class
+ *
+ * Returns a connection to a MySQL server or a specific database.
+ *
+ * For connection the MySQL++ library is used. MySQL++ throws exceptions
+ * in case of errors, e.g. connection timeout.<P>
+ *
+ * Note that although the object is created with 'new' and there
+ * is a 'delete' is JavaScript, it will not call any kind of
+ * destructor. To close a Subscription you have to explicitly call
+ * the close() member function. 'delete' in JavaScript is only
+ * to remove a property from an Object.
+ *
+ * @param {String} database
+ *    The databse argument is of this form (optional parts ar given in brackets):<br>
+ *    <tt>user:password@server.domain.com[:port]/database</tt>
+ *
+ * @throws
+ *    <li> If number or type of arguments is wrong
+ *    <li> If no connection could be opened, an exception with the reason is
+ *    thrown.
+ *
+ * @example
+ *    var db = new Database("thomas@sql.at-home.com/database");
+ */
+function Database()
+{
+    /**
+     * User connected to the database
+     * @constant
+     */
+    this.user = user;
+
+    /**
+     * Server which is connected
+     * @constant
+     */
+    this.server = server;
+
+    /**
+     * Database which is connected
+     * @constant
+     */
+    this.database = database;
+
+    /**
+     * Port connected (if no port was given 'undefined')
+     * @constant
+     */
+    this.port = port;
+
+    /**
+     * Returns the result of an sql query sent to the database.
+     *
+     * @param arguments
+     *    The arguments specify the query to be sent
+     *
+     * @throws
+     *    If no connection could be opened, an exception with the reason is
+     *    thrown.
+     *
+     * @returns
+     *    An array is returned. Each entry in the array corresponds to one
+     *    row of the table and is expressed an an associative array (object).
+     *    The names of the entries (columns) in each row are stored in
+     *    a property cols which is an array itself. For convenience,
+     *    table and query are stored in identically names properties.
+     *
+     * @example
+     *    var table = db.query("SELECT * FROM table WHERE value BETWEEN", 5, "AND 20");
+     *    for (var row=0; row&lt;table.length; row++)
+     *        for (var col in table.cols)
+     *            console.out("table["+row+"]['"+col+"']="+table[row][col]);
+     *
+     */
+    this.query = function() { /* [native code] */ }
+
+    /**
+     *
+     * Close the connection to the database.
+     *
+     * The connection is automaically closed at cript termination.
+     *
+     * @returns {Boolean}
+     *     If the connection was successfully closed, i.e. it
+     *     was still open, true is returned, false otherwise.
+     *
+     */
+    this.close = function() { /* [native code] */ }
+};
Index: branches/FACT++_scripts_refactoring/doc/Event.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Event.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Event.js	(revision 18221)
@@ -0,0 +1,119 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of the Event object returned by Subscription.get()
+ */
+
+/**
+ * @class
+ *
+ * The object returned by Subscription.get(). It contains
+ * all data received with the event.
+ *
+ */
+function Event()
+{
+    /**
+     * The name of the Subscription this event belongs to.
+     *
+     * @type String
+     * @constant
+     */
+    this.name = name;
+
+    /**
+     * The format string corresponding to this event.
+     *
+     * @see <A HREF="dim.cern.ch">DIM</A> for more details
+     * @type String
+     * @constant
+     */
+    this.format = format;
+
+    /**
+     * The Quality-of-Service transmitted by this event.
+     *
+     * @type Integer
+     * @constant
+     */
+    this.qos = qos;
+
+    /**
+     * The size in bytes of the event received
+     *
+     * @type Integer
+     * @constant
+     */
+    this.size = size;
+
+    /**
+     * An counter of events received since the Subscription has
+     * been created. The first event received is 1. 0 corresponds
+     * to no event received yet.
+     *
+     * @type Integer
+     * @constant
+     */
+    this.counter = counter;
+
+    /**
+     * The time transmitted with this event, if transmitted. If nonw
+     * was transmitted, this might just be the time the event was
+     * received.
+     *
+     * @type Date
+     * @constant
+     */
+    this.time = time;
+
+    /**
+     * Array with the data received.
+     *
+     * The contents of the array are sorted in the order of the event format
+     * string. The contents of the array can be all kind of objects
+     * defined by the format string. If a format described several entries
+     * (e.g. I:5) and array will be added.<P>
+     *
+     * In the special case that the format string contains only a single
+     * format, e.g. "I", "F:5" or "C", data will not be an array,
+     * but contain the object data (or the array) directly.
+     *
+     * If valid data was received, but the size was zero, then
+     * null is returned as data
+     *
+     *    <li> data===undefined: no data received (no connection)
+     *    <li> data===null:      an event was received, but it was empty
+     *    <li> data.length>0:    an event was received and it contains data
+     *
+     * @type Array
+     * @constant
+     *
+     */
+    this.data = [ ];
+
+    /**
+     * Object with the data received.
+     *
+     * The object contains essentially the same information than the
+     * data memeber, but the received data are added as properties
+     * instead of enumerable lements. This allows to access
+     * the received data by names as specified by the SERVICE_DESC
+     * service.<P>
+     *
+     * If an empty event was received, but names are available,
+     * the object will be empty. Otherwise 'obj' will be undefined.
+     *
+     *     <li> obj===undefined: no names are available
+     *     <li> obj!==undefined, length==0: names are available, but no data (no connection)
+     *     <li> obj!==undefined, length>0: names are available, data has been received
+     *
+     * <P>
+     * Note that to get the number of properties (length) you have to call
+     * Object.keys(obj).length;
+     *
+     * @type Object
+     * @constant
+     *
+     */
+    this.obj = { };
+}
Index: branches/FACT++_scripts_refactoring/doc/Local.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Local.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Local.js	(revision 18221)
@@ -0,0 +1,101 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of Local class built into dimctrl.
+ */
+
+/**
+ * @class
+ *
+ * A set of coordinates on the celestial sphere.
+ *
+ * The class stores a set of coordinates on the celestial, i.e. local,
+ * sky. If the data was the result of a coordinate transformation, the
+ * corresponding time is stored in addition. Functions to convert to sky
+ * coordinates and to measure distances on th sky are included.
+ *
+ * @param {Number} zenithDistance
+ *     Zenith angle in degree (Zenith=0deg)
+ *
+ * @param {Number} azimuth
+ *     Azimuth angle in degree (North=0deg, East=90deg)
+ *
+ * @example
+ *     var local = new Local(12, 45);
+ *     var sky   = local.toSky();
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ *
+ */
+function Local(zenithDistance, azimuth)
+{
+    /**
+     * Zenith distance in degree (Zenith=0deg)
+     *
+     * @constant
+     *
+     * @type Number
+     */
+    this.zd = zenithDistance;
+
+    /**
+     * Azimuth in degree (North=0deg, East=90deg)
+     *
+     * @constant
+     *
+     * @type Number
+     */
+    this.az = azimuth;
+
+    /**
+     * Time corresponding to ra and dec if they are the result of
+     * a conversion.
+     *
+     * @constant
+     * @default undefined
+     *
+     * @type Date
+     */
+    this.time = undefined;
+
+
+    /**
+     * Convert celestial coordinats to sky coordinates.
+     * As observatory location the FACT telescope is assumed.
+     * The conversion is done using libnova's ln_get_equ_from_hrz.
+     *
+     * @constant
+     *
+     * @param {Date} [time=new Date()]
+     *     Reference time for the conversion
+     *
+     * @returns {Sky}
+     *     A Sky object with the converted coordinates and
+     *     the corresponding time.
+     */
+    this.toSky = function() { /* [native code] */ }
+}
+
+/**
+ * Calculate the distance between two celestial sky positions.
+ *
+ * The distance between the two provided objects is calculated.
+ * The returned value is an absolute distance (angle) between
+ * the two positions.
+ *
+ * @constant
+ *
+ * @param {Local} local1
+ *     Celestial coordinates for one of the two objects for which
+ *     the distance on the sky should be calculated. In principle
+ *     every object with the properties 'zd' and 'az' can be provided.
+ *
+ * @param {Local} local2
+ *     Celestial coordinates for one of the two objects for which
+ *     the distance on the sky should be calculated. In principle
+ *     every object with the properties 'zd' and 'az' can be provided.
+ *
+ * @returns {Number}
+ *     Absolute distance between both positions on the sky in degrees.
+     */
+Local.dist = function() { /* [native code] */}
Index: branches/FACT++_scripts_refactoring/doc/Mail.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Mail.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Mail.js	(revision 18221)
@@ -0,0 +1,115 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    A class which allows to send mails through the 'mail' program
+ */
+
+
+/**
+ * @class
+ *
+ * This class represents an interface to the program 'mail'.
+ *
+ * To send a mail, create an instance and fill the properties
+ * (see reference) with proper data.
+ *
+ * @example
+ *     var mail = new Mail("This is the subject");
+ *
+ *     // At least one recipient is mandatory
+ *     mail.recipients.push("max.mustermann@musterstadt.com");
+ *     // To add several recipients
+ *     mail.recipients.push("max.mustermann@musterstadt.com", "erika.mustermann@musterstadt.com");
+ *
+ *     // similar to the property recipient you can use the properties 'cc' and 'bcc'
+ *
+ *     // If you want to add attachments [optional]
+ *     mail.attachments.push("logfile.txt");
+ *     // or for several attachments
+ *     mail.attachments.push("logfile1.txt", "logfile2.txt");
+ *
+ *     // The text of the message is set in the property text...
+ *     // ...either as single string
+ *     mail.text.push("This is line1\nThis is line2");
+ *     mail.text.push("This is line1");
+ *     mail.text.push("This is line2");
+ *
+ *     // Send the message
+ *     mail.send();
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ *
+ */
+function Mail()
+{
+
+    /**
+     * Subject of the mail
+     *
+     * @type String
+     * @constant
+     */
+    this.subject = subject;
+
+    /**
+     * Recipient(s) of the mail. One recipient is mandatory.
+     *
+     * @type Array[String]
+     */
+    this.recipients = recipient;
+
+    /**
+     * Carbon copy [optional]. Adresses who should receive a copy of the
+     * mail. All entries in the array which are not a string are silently ignored.
+     *
+     * @type Array[String]
+     */
+    this.cc = undefined;
+
+    /**
+     * Blind carbon copy [optional]. Adresses who should receive a copy of the
+     * mail. All entries in the array which are not a string are silently ignored.
+     *
+     * @type Array[String]
+     */
+    this.bcc = undefined;
+
+    /**
+     * Attachments [optional]. File to be attached to the mail.
+     * All entries in the array which are not a string are silently ignored.
+     *
+     * @type Array[String]
+     */
+    this.attachments = undefined;
+
+    /**
+     * Message body. At least one line in the message is mandatory.
+     * Undefined or null entries in the array are silently ignored.
+     *
+     * @type Array[String]
+     */
+    this.text = text;
+
+    /**
+     * Send the message. This calles the 'mailx' program. For further
+     * details, e.g. on the return value, see the corresponding man page.
+     *
+     * @param {Boolean} [block=true]
+     *    This parameter specifies whether the pipe should be closed,
+     *    which means that a blocking wait is performed until the 'mail'
+     *    program returns, or the pipe will be closed automatically
+     *    in the background when the 'mail' program has finished.
+     *    Note, that if the calling program terminates, maybe this
+     *    call will never succeed.
+     *
+     * @returns {Integer}
+     *    The return code of the 'mail' program is returned (0
+     *    usually means success), undefined if block was set to false.
+     *
+     * @throws
+     *    An exception is thrown if any validity check for the
+     *    properties or the arguments fail.
+     *
+     */
+    this.send = function() { /* [native code] */ }
+}
Index: branches/FACT++_scripts_refactoring/doc/Moon.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Moon.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Moon.js	(revision 18221)
@@ -0,0 +1,116 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of Moon class built into dimctrl.
+ */
+
+/**
+ * @class
+ *
+ * Calculates the moon's sky position at a given time.
+ *
+ * When instantiated, the class members are set to the sky position
+ * of the moon at the given time. The calculation is done using
+ * libnova's ln_get_lunar_equ_coords. A different time can be provided
+ * by the user. The sky coordinates are stored together with the time.
+ * A function is provided to convert the moon's position to celestial
+ * coordinates. A function to calculate the illuminated fraction of
+ * the moon's disk.
+ *
+ * @param {Date} [time=new Date()]
+ *    Reference time for the calculation of the moon's position.
+ *
+ * @example
+ *    var moon = new Moon();
+ *    var local = moon.toLocal();
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+function Moon(time)
+{
+    /**
+     * Right ascension of the moon in hours.
+     *
+     * @type Number
+     * @constant
+     *
+     */
+    this.ra = 0;
+
+    /**
+     * Declination of the moon in degrees.
+     *
+     * @type Number
+     * @constant
+     *
+     */
+    this.dec = 0;
+
+    /**
+     * Time corresponding to the calculated sky coordinates of the moon.
+     *
+     * @type Date
+     * @constant
+     */
+    this.time = time;
+
+    /**
+     * Converts the moon's sky coordinates to celestial coordinates.
+     * As observatory location the FACT telescope is assumed. For the
+     * time, the time corresponding to the stored sky coordinates is used.
+     * The conversion is done using libnova's ln_get_hrz_from_equ.
+     *
+     * @returns {Local}
+     *     A Local object with the converted coordinates and
+     *     the corresponding time.
+     */
+    this.toLocal = function() { /* [native code] */  }
+}
+
+/**
+ * Calculates the illuminated fraction of the moon's disk.
+ *
+ * Calculates the illuminated fraction of the moon's disk for the
+ * provided time. If no time is provided, the current system time
+ * is used. The calculation is done using libnova's ln_get_lunar_disk.
+ *
+ * @param {Date} [time=new Date()]
+ *    Time for which the moon disk should be calculated. If no time is
+ *    given, the current time is used.
+ *
+ * @type Number
+ *
+ * @returns
+ *    The illuminated fraction of the moon's disk corresponding
+ *    to the time argument
+ *
+ */
+Moon.disk = function() { /* [native code] */ }
+
+/**
+ * Calculate moon rise, set and transit times.
+ *
+ * Calculates the moon rise and set time, above and below horizon,
+ * and the time of culmination (transit time) for the given time.
+ * The calculation is done using libnova's ln_get_lunar_rst and is
+ * always performed for the FACT site at La Palma.
+ *
+ * @param {Date} [time=new Date()]
+ *    Date for which the times should be calculated. Note that the date
+ *    is converted to UTC and the times are calculated such that the
+ *    Date (yy/mm/dd) is identical for all returned values.
+ *
+ * @type {Object}
+ *
+ * @returns
+ *    An object with the following properties is returned: time {Date}
+ *    the provided time; rise, transit, set {Date} times of rise, set and
+ *    transit; isUp {Boolean} whether the moon is above or below horizon
+ *    at the provided time. If the moon does not rise or set, the properties
+ *    rise, transit and set will be undefined.
+ *
+ * @example
+ *    var date = new Date("2012-10-25 16:30 GMT"); // Date in UTC
+ *    console.out(JSON.stringify(Moon.horizon(date));
+ */
+Moon.horizon = function() { /* [native code] */ }
Index: branches/FACT++_scripts_refactoring/doc/Sky.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Sky.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Sky.js	(revision 18221)
@@ -0,0 +1,97 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of Sky class built into dimctrl.
+ */
+
+
+/**
+ * @class
+ *
+ * This class represents a set of sky coordinates.
+ *
+ * If the data was the result of a coordinate transformation, the
+ * corresponding time is stored in addition. A function to convert
+ * to local coordinates is included.
+ *
+ * @param {Number} rightAscension
+ *    Right ascension in hours
+ *
+ * @param {Number} declination
+ *    Declination in degree
+ *
+ * @example
+ *     var sky   = new Sky(12, 45);
+ *     var local = sky.toLocal();
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ *
+ */
+function Sky()
+{
+
+    /**
+     * Right ascension in hours
+     *
+     * @constant
+     * @type Number
+     */
+    this.ra = rightAscension
+
+    /**
+     * Declination in degrees
+     *
+     * @constant
+     * @type Number
+     */
+    this.dec = declination;
+
+    /**
+     * Time corresponding to ra and dec if they are the result of
+     * a conversion.
+     *
+     * @constant
+     * @type Date
+     */
+    this.time = undefined;
+
+    /**
+     * Convert sky coordinates to celestial coordinates.
+     * As observatory location the FACT telescope is assumed.
+     * The conversion is done using libnova's ln_get_hrz_from_equ.
+     *
+     * @param {Date} [time=new Date()]
+     *     Reference time for the converstion.
+     *
+     * @type Local
+     *
+     * @returns
+     *     A Local object with the converted coordinates and
+     *     the conversion time.
+     */
+    this.toLocal = function() { /* [native code] */  }
+}
+
+/**
+ * Calculate the distance between two sky positions.
+ *
+ * The distance between the two provided objects is calculated.
+ * The returned value is an absolute distance (angle) between
+ * the two positions.
+ *
+ * @constant
+ *
+ * @param {Sky} sky1
+ *     Celestial coordinates for one of the two objects for which
+ *     the distance on the sky should be calculated. In principle
+ *     every object with the properties 'ra' and 'dec' can be provided.
+ *
+ * @param {Sky} sky2
+ *     Celestial coordinates for one of the two objects for which
+ *     the distance on the sky should be calculated. In principle
+ *     every object with the properties 'ra' and 'dec' can be provided.
+ *
+ * @returns {Number}
+ *     Absolute distance between both positions on the sky in degrees.
+ */
+Sky.dist = function() { /* [native code] */}
Index: branches/FACT++_scripts_refactoring/doc/String.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/String.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/String.js	(revision 18221)
@@ -0,0 +1,122 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of the extension of the String class
+ *    built into dimctrl.
+ */
+
+/**
+ * Format a string (similar to printf in C).
+ *
+ * This function replaces modifiers (very similar to printf) with
+ * a formated version of the argument. Since JavaScript does implicit
+ * format conversion already, this is actually a very powerful tool,
+ * because the type of th arguments to be formated is of no importance.
+ * Implicit conversion means that also arrays and objects can be given
+ * as arguments. A shortcut is available as $-extension of the native
+ * String class.<P>
+ *
+ * Note that this function is completely written in JavaScript. It
+ * can be found in InterpreterV8.cc.<P>
+ *
+ * The following modifiers are available (optional parts are put in
+ * brackets:<br>
+ *
+ * <li><dt><B>c:</B> <tt>%[[-][0]N]c</tt></dt>
+ * <dd>Extracts the first element from an array. In case of a String this
+ * is the first character.</dd><p>
+ *
+ * <li><dt><B>s:</B> <tt>%[[-][0]N]s</tt></dt>
+ * <dd>Converts the argument to a string using toString()</dd><p>
+ *
+ * <li><dt><B>f:</B> <tt>%[[-][0]N[.n]]f</tt></dt>
+ * <dd>Converts to a Number value with n internal decimal places</dd><p>
+ *
+ * <li><dt><B>p:</B> <tt>%[[-][0]N[.n]]p</tt></dt>
+ * <dd>Converts to a Number value with a precision of n decimal places</dd><P>
+ *
+ * <li><dt><b>e:</b> <tt>%[[-][0]N]e</tt></dt>
+ * <dd>Converts to an exponential with a precision of n decimal places</dd><p>
+ *
+ * <li><dt><b>x:</b> <tt>%[[-][0]N[#b]x</tt></dt>
+ * <dd>Converts to an integer value using the basis b for conversion
+ * (default is 16 for hexadecimal)</dd><p>
+ *
+ * <li><dt><b>d:</b> <tt>%[[-][0]N[.n][#b]]d</tt></dt>
+ * <dd>Converts from a value using the basis b for conversion (default
+ * is 10 for decimal). The integer can be rounded to the given precision n.
+ * </dd><p>
+ *
+ * The output string will have at least a length of N. If 0 is given,
+ * it is filled with 0's instead of white spaces. If prefixed by a minus
+ * the contents will be left aligned.
+ *
+ * @param {String} format
+ *     The format string defining how the given argument elements are
+ *     formated
+ *
+ * @param {Array} elements
+ *     An array with the elements to be formated
+ *
+ * @returns {String}
+ *     The formated string is returned.
+ *
+ * @see
+ *     String.$
+ *
+ * @example
+ *    var result;
+ *    result = String.form("%5d %3d", [ 5, "2" ]);
+ *    result = String.form("%5x", [ 12 ]);
+ *    result = String.form("%#16d", [ "b" ]);
+ *    result = String.form("%s", [ [ 1, 2, 3, ] ]);
+ *    result = String.form("%s", [ { a:1, b:2, c:3, } ]);
+ *    var abbrev = "%03d".$(42);
+ *
+ */
+String.form = function() { /* [native code] */ }
+
+
+/**
+ * An abbreviation for String.form.
+ *
+ * Converts all arguments provided by the user into an array
+ * which is passed to String.form. The contents of the String itself
+ * is passed as format string. This allows a very compact syntax to
+ * format any kind of object, array or number.
+ * For details see String.form.
+ *
+ * @param   arg       An argument to be formated.
+ * @param   [. . .]   An undefined number of additional optional arguments.
+ *
+ * @returns {String} see String.form
+ * @throws  see String.form
+ * @see     String.form
+ *
+ * @example
+ *    var result = "%5d = %12s".$(5, "five");
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+String.prototype.$ = function() { /* [native code] */ };
+
+/**
+ * Like String match, but return the number counts how often
+ * the regular expression matches.
+ *
+ * @param {String} regex
+ *     The regular expression to search for, e.g., "s" (to count the s's) or
+ *     "As+A" (to count how often s's are surrounded by A's)
+ *
+ * @param {Boolean} [case=false]
+ *     Search is case insensitive if set to true.
+ *
+ * @returns {Interger}
+ *     The number of occurances of the regular expression
+ *
+ * @example
+ *    var result = "Thomas Bretz".count("[hme]"); // returns 3
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+String.prototype.count = function() { /* [native code] */ };
Index: branches/FACT++_scripts_refactoring/doc/Subscription.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Subscription.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Subscription.js	(revision 18221)
@@ -0,0 +1,156 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of a DIM service Subscription
+ */
+
+/**
+ * @class
+ *
+ * Subscription to a DIM service.
+ *
+ * This class represents the subscription to a DIM service. Received
+ * events are first copied to an even queue internally, to avoid
+ * any processing blocking the DIM thread (which could block the
+ * whole network as a result). Then the events are processed.
+ * If a callback is installed, the processing will take place in
+ * another JavaScript thread. Physically it will run synchronously
+ * with the other JavaScript threads. However, the processing blocks
+ * event processing. Consequetly, processing should on average be
+ * faster than the frequency with which events arrive to ensure they
+ * will not fill up the memory and possible reactions to there
+ * contents will happen within a reasonable time and not delayed
+ * too much.
+ *
+ * Each subscription must exist only once, therefore the function-call
+ * can be used to check for an open subscription.  
+ *
+ * @param {String} service
+ *    Name of the DIM service to which a subscription should be made.
+ *    Usully of the form SERVER/SUBSCRIPTION.
+ *
+ * @param {Function} [callback]
+ *    An optional function which is set as 'onchange' property.
+ *    This can avoid to loose th first event after the subscription
+ *    before the callback is set by the 'onchange' property (which
+ *    is very unlikely).
+ *
+ * @throws
+ *    <li>If number or type of arguments is wrong
+ *    <li>If an open subscription to the same service already exists.
+ *
+ * @example
+ *    var handle1 = Subscription("MAGIC_WEATHER/DATA");
+ *    if (!handle1)
+ *        handle1 = new Subscription("MAGIC_WEATHER/DATA");
+ *    var handle2 = new Subscription("TNG_WEATHER/DATA", function(evt) { console.out(JSON.stringify(evt)); });
+ *    ...
+ *    handle2.close();
+ *    handle1.close();
+ */
+function Subscription(service, callback)
+{
+    /**
+     *
+     * The name of the service subscribed to.
+     *
+     * @constant
+     * @type String
+     *
+     */
+    this.name = service;
+
+    /**
+     *
+     * Boolean value which is set to false if the Subscription was closed.
+     *
+     * @type Boolean
+     *
+     */
+    this.isOpen = false;
+
+    /**
+     *
+     * Callback in case of event reception.
+     *
+     * To install a callback in case a new event of this Subscription
+     * was received, set this property to a function. The argument
+     * provided to the function is identical with the object returned
+     * by Subscription.get(). For the code executed, the same rules apply
+     * than for a thread created with Thread.
+     *
+     * @type Function
+     *
+     * @example
+     *     handle.onchange = function(event) { console.out(JSON.stringify(event); };
+     *
+     */
+    this.onchange = callback;
+
+    /**
+     *
+     * Returns the last received event of this subscription.
+     *
+     * @param {Integer} [timeout=0]
+     *     A timeout in millisecond to wait for an event to arrive.
+     *     This timeout only applied if no event has been received yet
+     *     after a new Subscription has been created. If an event
+     *     is already available, the event is returned. If the timeout
+     *     is 'null', waiting will never timeout until an event was received.
+     *     If the timeout is less than zero, no exception will be thrown,
+     *     but 'undefined' returned in case of timeout. The corresponding
+     *     timeout is then Math.abs(timeout).
+     *
+     * @param {Boolean} [requireNamed=true]
+     *     Usually an event is only considered complete, if also the
+     *     corresponding decription is available distributed through
+     *     the service SERVER/SERVICE_DESC. If an event has no
+     *     description or access to the data by name is not important,
+     *     requireNamed can be set to false.
+     *
+     * @throws
+     *    <li> If number or type of arguments is wrong
+     *    <li> After a timeout, if the timeout value was greater or equal zero
+     *    <li> If conversion of the received data to an event object has failed
+     *
+     * @returns {Event}
+     *     A valid event is returned, undefined in the case waiting for an
+     *     event has timed out and exceptions are supressed by a negative
+     *     timeout.
+     *
+     * @example
+     *     var a = new Subscription("...service does not exist...");
+     *     a.get( 3000, true);  // throws an exception
+     *     a.get( 3000, false); // throws and exception
+     *     a.get(-3000, true);  // returns undefined
+     *     a.get(-3000, false); // returns undefined
+     *
+     *     var a = new Subscription("...service with valid description but no valid data yet...");
+     *     a.get( 3000, true);  // throws an exception
+     *     a.get( 3000, false); // returns Event.data==null, Event.obj valid but empty
+     *     a.get(-3000, true);  // return undefined
+     *     a.get(-3000, false); // returns Event.data==null, Event.obj valid but emoty
+     *
+     *     // Assuming that now valid description is available but data
+     *     var a = new Subscription("...service without valid description but valid data...");
+     *     a.get( 3000, true);  // throws an exception
+     *     a.get( 3000, false); // returns Event.data valid, Event.obj==undefined
+     *     a.get(-3000, true);  // returns undefined
+     *     a.get(-3000, false); // returns Event.data valid, Event.obj==undefined
+     *
+     */
+    this.get = function() { /* [native code] */ }
+
+    /**
+     *
+     * Unsubscribe from an existing subscription. Note that all open
+     * subscription produce network traffic and should be omitted if
+     * not needed.
+     *
+     * @returns {Boolean}
+     *     true if the subscription was still open, false if it was
+     *     already closed.
+     *
+     */
+    this.close = function() { /* [native code] */ }
+}
Index: branches/FACT++_scripts_refactoring/doc/Sun.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Sun.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Sun.js	(revision 18221)
@@ -0,0 +1,58 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of Sun class built into dimctrl.
+ */
+
+/**
+ * @namespace
+ *
+ * Namespace for functions returning astrometry information about the Sun.
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+var Sun = { };
+
+/**
+ * Calculate sun rise, set and transit times.
+ *
+ * Calculates the sun's rise and set time, above and below horizon,
+ * and the time of culmination (transit time) for the given time.
+ * As a second argument the angle abov or below horizon of interest
+ * can be provided. The calculation is done using libnova's
+ * ln_get_solar_rst_horizon and is always performed for the FACT
+ * site at La Palma.
+ *
+ * @param {Number,String} [angle=null]
+ *    Angle above (positive) or below (negative) horizon. The
+ *    angle can be given as Number in degree or as string referring to
+ *    "horizon" (0deg), "civil" (-6deg), "nautical" (-12deg),
+ *    "fact" (-15deg), "astronomical" (-18deg). Strings can be abbreviated
+ *    down to "h", "c", "n", "f" and "a". If the argument is omitted or
+ *    null, a value referring to the appearance or the disappearance of
+ *    the Sun's disk at horizon is chosen (~-0.8deg).
+ *
+ * @param {Date} [time=new Date()]
+ *    Date for which the times should be calculated. Note that the date
+ *    is converted to UTC and the times are calculated such that the
+ *    Date (yy/mm/dd) is identical for all returned values.
+ *
+ * @type {Object}
+ *
+ * @returns
+ *    An object with the following properties is returned: time {Date}
+ *    the provided time; rise, transit, set {Date} times of rise, set and
+ *    transit; horizon {Number} the angle used for the calculation;
+ *    isUp {Boolean} whether the sun is above or below the given horizon
+ *    at th given time. If the sun does not rise or set, the properties
+ *    rise, transit and set will be undefined, isUp will refer to
+ *    the fact whether the sun is the whole day above or below the
+ *    horizon (0deg).
+ *
+ * @example
+ *    var date = new Date("2012-10-25 16:30 GMT"); // Date in UTC
+ *    console.out(JSON.stringify(Sun.horizon());
+ *    console.out(JSON.stringify(Sun.horizon("astro"));
+ *    console.out(JSON.stringify(Sun.horizon(-12, date); // nautical
+ */
+Sun.horizon = function() { /* [native code] */ }
Index: branches/FACT++_scripts_refactoring/doc/Thread.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/Thread.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/Thread.js	(revision 18221)
@@ -0,0 +1,62 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of the Thread object
+ */
+
+/**
+ * @class
+ *
+ * Creates a handle to a new thread.
+ *
+ * The handle can be used to
+ * kill the thread or be ignored. The function provided is
+ * executed after an initial timeout. Note that although this
+ * looks similar to the setTimeout in web-browsers, after started,
+ * the thread will not run until completion but run in parallel to
+ * the executed script.<P>
+ *
+ * To stop the script from within a thread, use exit(). To stop only
+ * execution of the thread (silently) throw a null exception
+ * ("throw null;"). To terminate the script with an exception
+ * throw a normal exception ("throw new Error("my error");").
+ *
+ * Note that a running thread might consume all CPU. Although it is
+ * a seperated thread, JavaScript allows only one thread to run at
+ * a time (thus it can make programming simpler, but is not really
+ * consuming more CPU). In certain circumstances, it might be necessary
+ * to give CPU time with v8.sleep(...) back to allow other threads to run.
+ *
+ * @param {Integer} timeout
+ *    A positive integer given the initial delay in milliseconds before
+ *    the thread is executed.
+ *
+ * @param {Function} function
+ *    A function which is executed aftr the initial timeout.
+ *
+ * @param {Object} [_this]
+ *    An object which will be the reference for 'this' in the function call.
+ *    If none is given, the function itself will be the 'this' object.
+ *
+ * @throws
+ *    <li> If number or type of arguments is wrong
+ *
+ * @example
+ *    var handle = new Thread(100, function() { console.out("Hello world!"); });
+ *    ...
+ *    handle.kill();
+ */
+function Thread(timeout, function, _this)
+{
+    /**
+     *
+     * Kills a running thread
+     *
+     * @returns {Boolean}
+     *     If the thread was still known, true is returned, false
+     *     otherwise. If the thread terminated already, false is
+     *     returned.
+     *
+     */
+    this.kill = function() { /* [native code] */ }
+};
Index: branches/FACT++_scripts_refactoring/doc/_global_.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/_global_.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/_global_.js	(revision 18221)
@@ -0,0 +1,161 @@
+throw new Error("Description for built in functions. Must not be included!");
+/***************************************************************************/
+/***                                                                     ***/
+/***        JsDoc: http://code.google.com/p/jsdoc-toolkit/w/list         ***/
+/***                                                                     ***/
+/***                 jsdoc -d=html/dimctrl scripts/doc/                  ***/
+/***                                                                     ***/
+/***************************************************************************/
+/**
+ * @fileOverview
+ *    Documentation of the native functions built into dimctrl's
+ *    global namespace.
+ */
+
+/**
+ * An associative array containing the user supplied arguments identical to arg.
+ *
+ * @static
+ * @type Array
+ *
+ * @example
+ *    var value = $['name'];
+ *
+ */
+_global_.$ = [];
+
+/**
+ * An associative array containing the user supplied arguments identical to $.
+ *
+ * @static
+ * @type Array
+ *
+ * @example
+ *    for (var key in arg)
+ *        console.out("arg["+key+"]="+arg[key]);
+ */
+_global_.arg = [];
+
+/**
+ * A magic variable which is always set to the filename of the
+ * JavaScript file currently executed, if any.
+ *
+ * @static
+ * @type String
+ *
+ * @example
+ *    console.out(__FILE__);
+ */
+_global_.__FILE__ = filename;
+
+/**
+ * A magic variable which is always set to the modification time of the
+ * JavaScript file currently executed, if any.
+ *
+ * @static
+ * @type Date
+ *
+ * @example
+ *    console.out(__DATE__);
+ */
+_global_.__DATE__ = filedate;
+
+/**
+ * A magic variable which is always set to the start time when the
+ * current JavaScript session was started.
+ *
+ * @static
+ * @constant
+ * @type Date
+ *
+ * @example
+ *    console.out(__START__);
+ */
+_global_.__START__ = starttime;
+
+
+/**
+ * Includes another java script.
+ *
+ * Note that it is literally included,
+ * i.e. its code is executed as if it were at included at this
+ * place of the current file.
+ *
+ * @param {String} [name="test"]
+ *    Name of the file to be included. The base directory is
+ *    the directory in which dimctrl was started.
+ *
+ * @param {String} [. . . ]
+ *    More files to be included
+ *
+ * @type Array
+ *
+ * @static
+ *
+ */
+_global_.include = function() { /* [native code] */  }
+
+/**
+ * Forecefully exit the current script. This function can be called
+ * from anywhere and will terminate the current script.
+ *
+ * The effect is the same than throwing a null expecption ("throw null;")
+ * in the main thread. In every other thread or callback, the whole script
+ * will terminate which is different from the behaviour of a null exception
+ * which only terminates the corresponding thread.
+ *
+ * @static
+ *
+ */
+_global_.exit = function() { /* [native code] */  }
+
+/**
+ *
+ * @returns {String}
+ *    A string with the JavaScript V8 version is returned.
+ *
+ * @static
+ *
+ */
+_global_.version = function() { /* [native code] */  }
+
+/**
+ * Reads a file as a whole.
+ *
+ * Files can be split into an array when reading the file. It is
+ * important to note that no size check is done. So trying to read
+ * a file larger than the available memory will most probably crash
+ * the program. Strictly speaking only reading ascii fils make sense.
+ * Also gzip'ed files are supported.
+ *
+ * Note that this is only meant for debugging purpose and should
+ * not be usd in a production environment. Scripts should not
+ * access any files by defaults. If external values have to be
+ * provided arguments should be given to the script.
+ *
+ * @static
+ *
+ * @param {String} name
+ *    Name of the file to read. The base directory is the current
+ *    working directory
+ *
+ * @param {String} [delim=undefined]
+ *    A delimiter used to split the file into an array. If provided
+ *    it must be a String of length 1.
+ *
+ * @returns {String,Array[String]}
+ *    If no delimiter is given, a StringObject with the file (read
+ *    until \0) is returned. If a delimiter is given, an array
+ *    of Strings is returned, one for each chunk. Both objects
+ *    contain the property 'name' with the file name and the array
+ *    contains the property 'delim' with the used delimiter.
+ *
+ * @throws
+ *    <li> If number or type of arguments is wrong
+ *    <li> If there was an error reading the file, the system error is thrown
+ *
+ * @example
+ *    var string = File("fact++.rc");
+ *    var array  = File("fact++.rc", "\n");
+ */
+_global_.File = function() { /* [native code] */ }
Index: branches/FACT++_scripts_refactoring/doc/console.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/console.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/console.js	(revision 18221)
@@ -0,0 +1,53 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of dim namespace.
+ */
+
+/**
+ * @namespace
+ *
+ * Namespace for extension functions dealing with the console
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+var console = { };
+
+/**
+ *
+ * Displays a message on the local console (only).
+ *
+ * @param argument
+ *     Any kind of argument can be given. If it is not a String, it
+ *     is converted using the toString() member function.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments. Each argument will appear in
+ *     a new line.
+ *
+ * @example
+ *     console.out("Five="+5, "--- new line ---");
+ *
+ */
+console.out = function() { /* [native code] */ }
+
+/**
+ *
+ * Displays a warning message on the local console (only).
+ *
+ * (This is mainly meant for debugging purpose to highlight warning
+ *  messages.)
+ *
+ * @param argument
+ *     Any kind of argument can be given. If it is not a String, it
+ *     is converted using the toString() member function.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments. Each argument will appear in
+ *     a new line.
+ *
+ * @example
+ *     console.warn("WARNING: Five="+5, "--- new line ---");
+ *
+ */
+console.warn = function() { /* [native code] */ }
Index: branches/FACT++_scripts_refactoring/doc/dim.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/dim.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/dim.js	(revision 18221)
@@ -0,0 +1,260 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of dim namespace.
+ */
+
+/**
+ * @namespace
+ *
+ * Namespace for extension functions dealing with the DIM network.
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+var dim = { };
+
+/**
+ *
+ * Post a message into the dim log stream.
+ *
+ * It will be logged by the datalogger, displayed on the console
+ * and in the smartfact web-gui.
+ *
+ * @param argument
+ *     Any kind of argument can be given. If it is not a String, it
+ *     is converted using the toString() member function.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments. Each argument will appear in
+ *     a new line.
+ *
+ * @example
+ *     dim.log("Five="+5, "--- new line ---");
+ *
+ */
+dim.log = function() { /* [native code] */ }
+
+/**
+ *
+ * Posts a message to the dim network with alarm severity.
+ *
+ * Similar to dim.log, but the message is posted to the network
+ * with alarm severity. This means that it is displayed in red
+ * and the smartfact web-gui will play an alarm sound.
+ * The alarm state will stay valid (displayed in the web-gui) until it
+ * is reset.
+ *
+ * @param argument
+ *     Any kind of argument can be given. If it is not a String, it
+ *     is converted using the toString() member function.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments. Each argument will appear as
+ *     individual alarm.
+ *
+ * @example
+ *     dim.alarm("Alarm for 30 seconds!");
+ *     v8.sleep(30000);
+ *     dim.alarm();
+ */
+dim.alarm = function() { /* [native code] */ }
+
+/**
+ *
+ * Send a dim command to a dim client.
+ *
+ * @param {String} commandId
+ *     The command id is a string and usually compiles like
+ *     'SERVER/COMMAND'
+ *
+ * @param argument
+ *     Any kind of argument can be given. Arguments are internally
+ *     converted into strings using toString() and processed as
+ *     if they were typed on th console.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments.
+ *
+ * @example
+ *     dim.send('DRIVE_CONTROL/TRACK_SOURCE 0.5 180 "Mrk 421"');
+ *     dim.send('DRIVE_CONTROL/TRACK_SOURCE', 0.5, 180, 'Mrk 421');
+ *
+ * @returns
+ *     A boolean value is returned whether the command was succesfully
+ *     posted into the network or not. Note that true does by no means
+ *     mean that the command was sucessfully received or even processed.
+ */
+dim.send = function() { /* [native code] */ }
+
+/**
+ * Returns the state of the given server.
+ *
+ * @param {String} name
+ *     The name of the server of which you want to get the state.
+ *
+ * @throws
+ *    If number or type of arguments is wrong
+ *
+ * @returns {Object}
+ *     An object with the properties 'index' {Integer} and 'name' {String}
+ *     is returned if a connection to the server is established and
+ *     state information have been received, 'undefined' otherwise. If
+ *     the time of the last state change is available, it is stored
+ *     in the 'property'. If a server disconnected, a valid object will
+ *     be returned, but without any properties.
+ */
+dim.state = function() { /* [native code] */ }
+
+/**
+ * Wait for the given state of a server.
+ *
+ * Note that the function is internally asynchornously checking the
+ * state, that means that theoretically, a state could be missed if
+ * it changes too fast. If this can happen callbacks have to be used.
+ *
+ * @param {String} name
+ *     The name of the server of which you want to wait for a state.
+ *     The name must not contain quotation marks. To wait for
+ *     "not the given state", prefix the server name with an
+ *     exclamation mark, e.g. "!DIM_CONTROL"
+ *
+ * @param {String,Integer} state
+ *     The state you want to wait for. It can either be given as an Integer
+ *     or as the corresponding short name. If given as String it must
+ *     not contain quotation marks.
+ *
+ * @param {Integer} [timeout]
+ *     An optional timeout. If no timeout is given or a timeout of undefined,
+ *     waiting will not stop until the condition is fulfilled. A timeout
+ *     of 0 is allowed and will essentially just check if the server is
+ *     in this state or not. If a negative value is given, exceptions are
+ *     suppressed and false is returned in case of timeout. As timeout
+ *     the absolute value is used.
+ *
+ * @throws
+ *    <li> If number or type of arguments is wrong
+ *    <li> If no connection to the server is established or no state
+ *         has been received yet. This is identical to dim.state()
+ *         returning 'undefined' (only in case a positive timeout 
+ *         is given)
+ *
+ * @returns {Boolean}
+ *     true if the state was achived within the timeout, false otherwise.
+ */
+dim.wait = function() { /* [native code] */ }
+
+/**
+ *
+ * Returns a list of all known state of a given server.
+ *
+ * The returned object has all states with their index as property.
+ * Each state is returned as a String object with the property description.
+ *
+ * @param {String} [server='DIM_CONTROL']
+ *     Name of the server for which states should be listed.
+ *     The states of the DIM_CONTROl server are the default.
+ *
+ * @throws
+ *    If number or type of arguments is wrong
+ *
+ * @type Object[StringObject]
+ *
+ * @example
+ *     var states = dim.getStates("SERVER");
+ *     console.out(JSON.stringify(states));
+ *     for (var index in states)
+ *         console.out(index+"="+states[index]+": "+states[index].description);
+ */
+dim.getStates = function() { /* [native code] */ }
+
+/**
+ *
+ * Returns a description for one service
+ *
+ * The returned array has objects with the properties: name, description and unit. The last
+ * two are optional. The array itself has the properties name, server, service, isCommand
+ * and optionally format.
+ *
+ * @param {String} service
+ *     String specifying the name of the service to be returned.
+ *
+ * @throws
+ *    If number or type of arguments is wrong
+ *
+ * @type {Description}
+ *
+ * @example
+ *     var s = dim.getDescription("TNG_WEATHER/DATA");
+ *     console.out("Name="+s.name);
+ *     console.out("Server="+s.server);
+ *     console.out("Service="+s.service);
+ *     console.out("Format="+s.format);
+ *     console.out("Description="+s.name);
+ *     console.out("IsCommand="+s.isCommand);
+ *     console.out(JSON.stringify(s));
+ */
+dim.getDescription = function() { /* [native code] */ }
+
+/**
+ *
+ * Returns a list of all known services
+ *
+ * The returned array has objects with the properties: name, server, service, command
+ * and (optional) format.
+ *
+ * @param {String} [service='*']
+ *     String a service has to start with to be returned. The default is to return
+ *     all available services. An empty string or '*' are wildcards for all
+ *     services.
+ *
+ * @param {Boolean} [isCommand=undefined]
+ *     If no second argument is specified, data services and commands are returned.
+ *     With true, only commands, with false, only data-services are returned.
+ *
+ * @throws
+ *    If number or type of arguments is wrong
+ *
+ * @type Array[Object]
+ *
+ * @example
+ *     // Return all services of the FAD_CONTROL starting with E
+ *     var services = dim.getServices("FAD_CONTROL/E");
+ *     console.out(JSON.stringify(services));
+ */
+dim.getServices = function() { /* [native code] */ }
+
+/**
+ *
+ * Callback in case of state changes.
+ *
+ * To install a callback in case the state of a server changes. set
+ * the corresponding property of this array to a function. The argument
+ * provided to the function is identical with the object returned
+ * by dim.state(). In addition the name of the server is added
+ * as the 'name' property and the comment sent with the state change
+ * as 'comment' property. For the code executed, the same rules apply
+ * than for a thread created with Thread.<P>
+ *
+ * If a state change is defined for a server for which no callback
+ * has been set, the special entry '*' is checked.
+ *
+ *
+ * @type Array[Function]
+ *
+ * @example
+ *     dim.onchange['*'] = function(state) { console.out("State change from "+state.name+" received"); }
+ *     dim.onchange['DIM_CONTROL'] = function(state) { console.out(JSON.stringify(state); }
+ *     ...
+ *     delete dim.onchange['DIM_CONTROL']; // to remove the callback
+ *
+ */
+dim.onchange = [];
+
+
+/**
+ * DIM version number
+ *
+ * @constant
+ * @type Integer
+ */
+dim.version = 0;
Index: branches/FACT++_scripts_refactoring/doc/dimctrl.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/dimctrl.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/dimctrl.js	(revision 18221)
@@ -0,0 +1,157 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of the dimctrl namespace
+ */
+
+/**
+ * @namespace
+ *
+ * Global namespace for functions dealing with the dimctrl state
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+var dimctrl = { };
+
+/**
+ * Define a new internal state.
+ *
+ * States should be defined when a script is started.
+ *
+ * @param {Integer} index
+ *    The intgeger number assigned to the new state. Only numbers
+ *    in the range [10, 255] are allowed.
+ *
+ * @param {String} [name]
+ *    A short name describing the state. According the the convention
+ *    used throughout FACT++, it it must not contain whitespaces or
+ *    underscores. Ever word should start with a capital letter,
+ *    e.g. 'TriggerOn'
+ *
+ * @param {String} [decription]
+ *    A user defined string which gives a more conscise explanation
+ *    of the meaning of the state and can also be displayed in the GUI
+ *    or anywhere else automatically,
+ *    e.g. "System setup and trigger switched on"
+ *
+ * @throws
+ *    <li> if something is wrong with the supplied arguments (type, number)
+ *    <li> when the index is out of range [10,255]
+ *    <li> the given state name is empty
+ *    <li> the given state name contains a colon or an equal sign
+ *    <li> when a state with the same name or index was already
+ *    <li> set since the script was started.
+ *
+ * @returns {Boolean}
+ *    A boolean whether the state was newly added (true) or an existing
+ *    one overwritten (false).
+ *
+ * @example
+ *     dim.defineState(10, "StateTen", "This is state number ten");
+ */
+dimctrl.defineState = function() { /* [native code] */ }
+
+/**
+ * Change the internal state.
+ *
+ * @param {Integer,String} state
+ *    Either the name of the state to set or its index can be supplied.
+ *
+ * @throws
+ *    <li> if something is wrong with the supplied arguments (type, number)
+ *    <li> if a String is given and it is not found in the list of names
+ *
+ * @returns {Boolean}
+ *     A boolean is returned whether setting the state wa sucessfull or
+ *     not. If the function is not called at unexpected time, i.e.
+ *     before the execution of the JavaScript has been started or
+ *     after it has already be terminated, true should be returned
+ *     always.
+ *
+ * @example
+ *     dim.setState(10);
+ *     dim.setState("StateTen");
+ */
+dimctrl.setState = function() { /* [native code] */ }
+
+/**
+ * Get the current internal state.
+ *
+ * @throws
+ *    if arguments are supplied
+ *
+ * @returns {Object}
+ *     An object with the properties index {Number}, name {String} and
+ *     description {String}. Note that name and description might be
+ *     an empty string.
+ *
+ * @example
+ *     var state = dim.getState();
+ *     console.out(JSON.stringify(state));
+ */
+dimctrl.getState = function() { /* [native code] */ }
+
+/**
+ * Set an interrupt handler, a function which is called if an
+ * interrupt is received, e.g. via dim (dimctrl --interrupt).
+ * Note that the interrupt handler is executed in its own JavaScript
+ * thread. Thus it interrupts the execution of the script, but does
+ * not stop its execution. Please also note that this is a callback
+ * from the main loop. As long as the handler is executed, no other
+ * event (dim or command interface) will be processed.
+ *
+ * If an interrupt was triggered by dimctrl (so not from within
+ * the script) and a number between 10 and 255 is returned,
+ * the state machine will change its state accordingly. Other returned
+ * ojects or returned values outside of this range are ignored.
+ *
+ * @param {Function} [func]
+ *    Function to be called when an interrupt is received. Null, undefined
+ *    or no argument to remove the handler.
+ *
+ * @throws
+ *    if number of type of arguments is wrong
+ *
+ * @example
+ *     function handleIrq(irq, args, time, user)
+ *     {
+ *         console.out("IRQ received:   "+irq);
+ *         console.out("Interrupt time: "+time);
+ *         console.out("Issuing user:   "+user);
+ *         console.out("Arguments:");
+ *         for (var key in args)
+ *             console.out(" args["+key+"="+args[key]);
+ *
+ *         var newState = 10;
+ *         return newState;
+ *     }
+ *     dimctrl.setInterruptHandler(handleIrq);
+ */
+dimctrl.setInterruptHandler = function() { /* [native code] */ }
+
+/**
+ * You can also issue an interrupt from anywhere in your code.
+ *
+ * @param argument
+ *     Any kind of argument can be given. If it is not a String, it
+ *     is converted using the toString() member function. The result
+ *     must not contain any line break.
+ *
+ * @param [. . .]
+ *     Any number of additional arguments. Each argument will appear in
+ *     a new line.
+ *
+ * @returns
+ *    the return of the interrupt handler which is called is returned
+ *
+ * @throws
+ *    if an argument contains a line break
+ *
+ * @example
+ *     dimctrl.triggerInterrupt();
+ *     dimctrl.triggerInterrupt("my_command");
+ *     dimctrl.triggerInterrupt("my_command arg1 arg2=x arg3");
+ *     dimctrl.triggerInterrupt("arg1=x arg2 arg3");
+ *
+ */
+dimctrl.triggerInterrupt = function() { /* [native code] */ }
Index: branches/FACT++_scripts_refactoring/doc/v8.js
===================================================================
--- branches/FACT++_scripts_refactoring/doc/v8.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/doc/v8.js	(revision 18221)
@@ -0,0 +1,69 @@
+throw new Error("Description for built in functions. Must not be included!");
+/**
+ * @fileOverview
+ *    Documentation of dim namespace.
+ */
+
+/**
+ * @namespace
+ *
+ * Namespace for general extension functions
+ *
+ * @author <a href="mailto:thomas.bretz@epfl.ch">Thomas Bretz</a>
+ */
+var v8 = { };
+
+/**
+ * Sleep for a while. This can be used to just wait or give time
+ * back to the operating system to produce less CPU load if the
+ * main purpose of a loop is, e.g., to wait for something to happen.
+ *
+ * @param {Integer} [milliseconds=0]
+ *     Number of millliseconds to sleep. Note that even 0 will always
+ *     sleep at least one millisecond.
+ *
+ */
+v8.sleep = function() { /* [native code] */ }
+
+/**
+ * This function implements a simple timeout functionality. 
+ * back to the operating system to produce less CPU load if the
+ * main purpose of a loop is, e.g., to wait for something to happen.
+ *
+ * @param {Integer} milliseconds
+ *     Number of millliseconds until the timeout. Note that even 0
+ *     will execute the function at least once. If the timeout
+ *     is negative no exception will be thrown by undefined will
+ *     be returned in case of a timeout.
+ *
+ * @param {Function} func
+ *     A function. The function defines when the conditional to end
+ *     the timeout will be fullfilled. As soon as the function returns
+ *     a defined value, i.e. something else than undefined, the
+ *     timeout is stopped and its return value is returned.
+ *
+ * @param {Object} [_this]
+ *    An object which will be the reference for 'this' in the function call.
+ *    If none is given, the function itself will be the 'this' object.
+ *
+ * @param [. . .]
+ *     Any further argument will be passed as argument to the function.
+ *
+ * @returns
+ *     Whatever is returned by the function. undefined in case of timeout
+ *     and a negative timeout value.
+ *
+ * @throws
+ *     <li> When the number or type of argument is wrong
+ *     <li> In case the timeout is positive and the timeout condition occurs
+ *
+ */
+v8.timeout = function() { /* [native code] */ }
+
+/**
+ * Version number of the V8 JavaScript engine.
+ *
+ * @constant
+ * @type String
+ */
+v8.version = "";
Index: branches/FACT++_scripts_refactoring/getSchedule.js
===================================================================
--- branches/FACT++_scripts_refactoring/getSchedule.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/getSchedule.js	(revision 18221)
@@ -0,0 +1,92 @@
+// ======================================================================================
+
+function getSchedule()
+{
+    // List of all available measurement types (see also Observation_class.js)
+    var measurementType = [ "STARTUP", "IDLE", "DRSCALIB", "SINGLEPE", "DATA", "RATESCAN", "SHUTDOWN", "OVTEST", "RATESCAN2", "SLEEP", "CUSTOM" ];
+
+    // Get current time
+    var start = new Date();//new Date("2013-04-07 19:00:00 UTC");
+
+    // Because Main.js could start a new observations just in the moment between 'now'
+    // and entering the new data in the database, we have to use the unique id
+    // in Main.js to check if the current observation should be changed (and sub resetted)
+    start = new Date(start.getTime()-10*3600000);
+
+    // ----------------------------------------------------------------------
+
+    // Connect to database
+    var db = new Database($['schedule-database']);
+
+    // get all sources from database
+    var sources = db.query("SELECT * from Source");
+
+    // Get the current schedule
+    var rows = db.query("SELECT * FROM Schedule WHERE fStart>'"+start.toISOString()+"' ORDER BY fStart, fMeasurementID");
+
+    // Close db connection
+    db.close();
+
+    // ----------------------------------------------------------------------
+
+    var schedule = [];
+    var entry    = -1;
+    var sub      =  0;
+
+    for (var i=0; i<rows.length; i++)
+    {
+        var sub = rows[i]['fMeasurementID'];
+        if (sub==0)
+            entry++;
+
+        var m = { }
+
+        var task = rows[i]['fMeasurementTypeKey'];
+        m.task = measurementType[task];
+
+        var src = rows[i]['fSourceKey'];
+        if (src)
+        {
+            // Convert SourceKey to SourceName
+            var arr = sources.filter(function(e) { return e['fSourceKEY']==src; });
+            if (arr.length==0)
+                throw new Error("SourceKey "+src+" unknown.");
+
+            m.source = arr[0]['fSourceName'];
+        }
+
+        var data = rows[i]['fData'];
+        if (data)
+        {
+            var obj = JSON.parse(("{"+data+"}").replace(/\ /g, "").replace(/(\w+):/gi, "\"$1\":"));
+            for (var key in obj)
+                m[key] = obj[key];
+        }
+
+        if (!schedule[entry])
+            schedule[entry] = { };
+
+        schedule[entry].id   = rows[i]['fScheduleID'];
+        schedule[entry].date = new Date(rows[i]['fStart']+" UTC");
+
+        if (!schedule[entry].measurements)
+            schedule[entry].measurements = [];
+
+        schedule[entry].measurements[sub] = m;
+    }
+
+    for (var i=0; i<schedule.length; i++)
+        schedule[i] = new Observation(schedule[i]);
+
+    return schedule;
+}
+
+// -------------------------------------------------------------------------------------
+
+/*
+ // remove "
+ conv = conv.replace(/\"(\w+)\":/ig, "$1:");
+ // must contain one , less than :
+ // must not contain " and '
+ //var test = "{ra:12,dec:13}".replace(/\ /g, "").replace(/(\w+):/gi, "\"$1\":");
+*/
Index: branches/FACT++_scripts_refactoring/handleAgilentPowerOn.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleAgilentPowerOn.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleAgilentPowerOn.js	(revision 18221)
@@ -0,0 +1,37 @@
+'use strict';
+
+// 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.log("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'... sending SET_POWER ON... waiting for 'VoltageOn'.");
+        dim.send("AGILENT_CONTROL/SET_POWER", true);
+        return "VoltageOn";
+
+    case "VoltageOn":
+        return "";
+
+    case "VoltageHigh":
+        throw new Error("Agilent reports voltage above limit ('VoltageHigh')... please check.");
+    }
+
+    throw new Error("AGILENT_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: branches/FACT++_scripts_refactoring/handleBiasVoltageOff.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleBiasVoltageOff.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleBiasVoltageOff.js	(revision 18221)
@@ -0,0 +1,50 @@
+'use strict';
+
+// 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.log("BIAS_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "Connecting":
+    case "Initializing":
+    case "Connected":
+    case "Ramping":
+        return wait_state;
+
+    case "Locked":
+        console.out("WARNING - Bias is LOCKED. Please report, this is serious, unlock manually and go on.");
+        return "";
+
+    // Do-something conditions
+    case "Disconnected":
+        console.out("Bias in Disconnected... 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: branches/FACT++_scripts_refactoring/handleDriveArmed.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleDriveArmed.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleDriveArmed.js	(revision 18221)
@@ -0,0 +1,52 @@
+'use strict';
+
+// 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.log("DRIVE_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+    case "Connected":
+    case "Ready":
+        v8.sleep(1000);
+        return undefined;
+
+    case "Locked":
+        //console.warn("Drive is LOCKED. Not all observation types will be supported.");
+        console.out("WARNING - Drive is LOCKED. Please unlock manually.");
+        return "";
+        //throw new Error("Drive is LOCKED. Please unlock manually.");
+        //return undefined;
+        //dim.log("Drive in Locked... unlock.");
+        //dim.send("DRIVE_CONTROL/UNLOCK");
+        //return "Armed";
+
+    case "Moving":
+    case "Tracking":
+    case "OnTrack":
+        console.out("Drive in '"+state.name+"'... sending STOP... waiting for 'Armed'.");
+        dim.send("DRIVE_CONTROL/STOP");
+        return "Armed";
+
+    case "NotReady":
+    case "ERROR":
+        console.out("Drive in '"+state.name+"'... sending STOP.");
+        dim.send("DRIVE_CONTROL/STOP");
+        dim.wait("DRIVE_CONTROL", "Armed", 60000);
+        return undefined;  // Process that again if necessary
+
+    case "Armed":
+        return "";
+    }
+
+    throw new Error("DRIVE_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: branches/FACT++_scripts_refactoring/handleFadConnected.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleFadConnected.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleFadConnected.js	(revision 18221)
@@ -0,0 +1,65 @@
+'use strict';
+
+// 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.log("BIAS_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Offline":
+        return undefined;
+
+    case "Disconnected":
+        console.out("Fadctrl in 'Disconnected'... sending START... waiting for 'Connected'.");
+        dim.send("FAD_CONTROL/START");
+        return "Connected";
+
+    // Do-nothing conditions
+    case "Connecting":
+        return wait_state;
+
+    // Do-something conditions
+    case "Configuring1":
+    case "Configuring2":
+    case "Configuring3":
+    case "Configured":
+        console.out("Fadctrl in Configure state... sending RESET_CONFIGURE... waiting for 'Connected'.");
+        dim.send("FAD_CONTROL/RESET_CONFIGURE");
+        return "Connected";
+
+    case "Disengaged":
+        console.out("Fadctrl in 'Disengaged'... sending START... waiting for 'Connected'.");
+        dim.send("FAD_CONTROL/START");
+        return "Connected";
+
+    case "RunInProgress":
+        console.out("Fadctrl in 'RunInProgress'... sending CLOSE_OPEN_FILES... waiting for 'Connected'.");
+        dim.send("FAD_CONTROL/CLOSE_OPEN_FILES");
+        return "Connected";
+
+    // Final state reached condition
+    case "Connected":
+        var sub_con = new Subscription("FAD_CONTROL/CONNECTIONS");
+        var con = sub_con.get(5000);
+        var all = true;
+        for (var i=0; i<40; i++)
+            if (con.obj['status'][i]&66!=66)
+            {
+                console.out("Board "+i+" not connected... sending CONNECT... waiting for 'Connected'.");
+                dim.send("FAD_CONTROL/CONNECT", i);
+                all = false;
+            }
+        sub_con.close();
+        return all ? "" : "Connected";
+    }
+
+    throw new Error("FAD_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: branches/FACT++_scripts_refactoring/handleFeedbackConnected.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleFeedbackConnected.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleFeedbackConnected.js	(revision 18221)
@@ -0,0 +1,41 @@
+'use strict';
+
+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.log("FEEDBACK:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+    case "Connecting":
+        return undefined;
+
+    case "Connected":
+    case "Calibrated":
+        return "";
+
+    case "WaitingForData":
+    case "OnStandby":
+    case "InProgress":
+    case "Warning":
+    case "Critical":
+        console.out("Feedback in '"+state.name+"'... sending STOP... waiting for 'Calibrated'.");
+        dim.send("FEEDBACK/STOP");
+        return "Calibrated";
+
+    case "Calibrating":
+        console.out("Feedback in '"+state.name+"'... sending STOP... waiting for 'Connected'.");
+        dim.send("FEEDBACK/STOP");
+        return "Connected";
+    }
+
+    throw new Error("FEEDBACK:"+state.name+"["+state.index+"] unknown or not handled.");
+}
+
Index: branches/FACT++_scripts_refactoring/handleFscConnected.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleFscConnected.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleFscConnected.js	(revision 18221)
@@ -0,0 +1,28 @@
+'use strict';
+
+// 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("Fscctrl in 'Disconnected'... sending RECONNECT... waiting for 'Connected'.");
+        dim.send("FSC_CONTROL/RECONNECT");
+        return "Connected";
+
+    case "Connected":
+        return "";
+    }
+
+    throw new Error("FSC_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: branches/FACT++_scripts_refactoring/handleFtmIdle.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleFtmIdle.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleFtmIdle.js	(revision 18221)
@@ -0,0 +1,54 @@
+'use strict';
+
+// 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.log("FTM_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "Disconnected":
+        console.out("Ftmctrl in 'Disconnected'... sending RECONNECT... waiting for 'Valid'.");
+        dim.send("FTM_CONTROL/RECONNECT");
+        return "Valid";
+
+    case "Idle":
+        console.out("Ftmctrl in 'Idle'... sending DISCONNECT... waiting for 'Disconnected'.");
+        dim.send("FTM_CONTROL/DISCONNECT");
+        v8.sleep(3000);
+        return "Disconnected";
+
+    case "Valid":
+        return "";
+
+    case "TriggerOn":
+        console.out("Ftmctrl in 'TriggerOn'... sending STOP_TRIGGER... waiting for 'Valid'.");
+        dim.send("FTM_CONTROL/STOP_TRIGGER");
+        return "Valid";
+ 
+    case "Configuring1":
+    case "Configuring2":
+    case "Configured1":
+        console.out("Ftmctrl in '"+state.name+"'... sending RESET_CONFIGURE... waiting for 'Valid'.");
+        dim.send("FTM_CONTROL/RESET_CONFIGURE");
+        return "Valid";
+
+    case "Configured2":
+        return "TriggerOn";
+
+    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: branches/FACT++_scripts_refactoring/handleFtuCheck.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleFtuCheck.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleFtuCheck.js	(revision 18221)
@@ -0,0 +1,32 @@
+'use strict';
+
+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.log("Problems in the FTU communication found.");
+        dim.log("Send command to disable all FTUs.");
+        dim.log(" => Power cycle needed.");
+        dim.send("FTM_CONTOL/ENABLE_FTU", -1, false);
+        throw new Error("CrateReset[FTU]");
+    }
+
+    return "";
+}
Index: branches/FACT++_scripts_refactoring/handleLidClosed.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleLidClosed.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleLidClosed.js	(revision 18221)
@@ -0,0 +1,38 @@
+'use strict';
+
+// 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.log("LID_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "NotReady":
+    case "Ready":
+    case "NoConnection":
+    case "Connected":
+    case "Moving":
+        return wait_state;
+
+    case "Unknown":
+    case "Inconsistent":
+    case "PowerProblem":
+    case "Open":
+        console.out("Lidctrl in '"+state.name+"'... sending CLOSE... waiting for 'Closed'.");
+        dim.send("LID_CONTROL/CLOSE");
+        return "Closed";
+
+    case "Closed":
+        return "";
+    }
+
+    throw new Error("LID_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
Index: branches/FACT++_scripts_refactoring/handlePwrCameraOn.js
===================================================================
--- branches/FACT++_scripts_refactoring/handlePwrCameraOn.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handlePwrCameraOn.js	(revision 18221)
@@ -0,0 +1,52 @@
+'use strict';
+
+// 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.log("PWR_CONTROL:  "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    // Do-nothing conditions
+    case "Disconnected":
+    case "Connected":
+    case "NoConnection":
+        return undefined;
+
+    // Drive off
+    case "PowerOff":
+        console.out("Pwrctrl in 'PowerOff'... sending CAMERA_POWER ON... waiting for 'DriveOff'.");
+        dim.send("PWR_CONTROL/CAMERA_POWER", true);
+        return "DriveOff";
+
+    // Drive on
+    case "DriveOn":
+        console.out("Pwrctrl in 'DriveOn'... sending CAMERA_POWER ON... waiting for 'SystemOn'.");
+        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: branches/FACT++_scripts_refactoring/handleRatectrlConnected.js
===================================================================
--- branches/FACT++_scripts_refactoring/handleRatectrlConnected.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/handleRatectrlConnected.js	(revision 18221)
@@ -0,0 +1,33 @@
+'use strict';
+
+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.log("RATE_CONTROL: "+state.name+"["+state.index+"]");
+
+    switch (state.name)
+    {
+    case "DimNetworkNotAvailable":
+    case "Disconnected":
+        return undefined;
+
+    case "Calibrating":
+    case "GlobalThresholdSet":
+    case "InProgress":
+        console.out("Ratectrl in '"+state.name+"'... sending STOP... waiting for 'Connected'.");
+        dim.send("RATE_CONTROL/STOP");
+        return "Connected";
+
+    case "Connected":
+        return "";
+    }
+
+    throw new Error("RATE_CONTROL:"+state.name+"["+state.index+"] unknown or not handled.");
+}
+
Index: branches/FACT++_scripts_refactoring/schedule.js_template
===================================================================
--- branches/FACT++_scripts_refactoring/schedule.js_template	(revision 18221)
+++ branches/FACT++_scripts_refactoring/schedule.js_template	(revision 18221)
@@ -0,0 +1,60 @@
+'use strict';
+
+// This list contains the schedule for one or several nights.
+// The schedule consists of observations and measurements.
+// An observation is compiled by several measurements.
+// Most measurements (like RATESCAN) cannot be interrupted, but
+// will be finished at some point (like a single run).
+// A measurement which takes until the next observation is started is DATA.
+// Whenever a measurement is finished and the start time of a new
+// observation has passed, the new observation is started.
+// In an observation it makes only sense that the last measurment
+// is data. All previous measurement just take as much time as they take.
+// Note that after each measurement, a new observation might be started
+// if the start time of the new observation has passed. Thus there is,
+// strictly speaking, no gurantee that any other than the first measurement
+// of one observation will ever be excuted.
+// A list of observations must end with a shutdown.
+//
+// here is an example:
+//
+var observations =
+[
+ { date:"2013-03-14 19:55 UTC", measurements:
+     [
+      { task:'startup' }
+     ]
+ },
+
+ { date:"2013-03-14 20:05 UTC", measurements:
+     [
+      { task:'data', source:'Crab' }
+     ]
+ },
+
+ { date:"2013-03-14 23:58 UTC", measurements:
+     [
+      { task:'ratescan', ra:9.438888, dec:29.0 },
+      { task:'data',     source:'Crab'        }
+     ]
+ },
+
+ { date:"2013-03-15 00:45 UTC", measurements:
+     [
+      { task:'ratescan', ra:11.26888888, dec:28.4477777 },
+      { task:'data',     source:'Mrk 421'}
+     ]
+ },
+
+ { date:"2013-03-15 03:30 UTC", measurements:
+     [
+      { task:'data', source:'Mrk 501'}
+     ]
+ },
+
+ { date:"2013-03-15 06:38 UTC", measurements:
+     [
+      { task:'shutdown'}
+     ]
+ },
+];
Index: branches/FACT++_scripts_refactoring/takeRun.js
===================================================================
--- branches/FACT++_scripts_refactoring/takeRun.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/takeRun.js	(revision 18221)
@@ -0,0 +1,333 @@
+'use strict';
+
+// ================================================================
+//  Code related to monitoring the fad system
+// ================================================================
+
+var incomplete = 0;
+
+sub_incomplete.onchange = function(evt)
+{
+    if (!evt.data)
+        return;
+
+    var inc = evt.obj['incomplete'];
+    if (!inc || inc>0xffffffffff)
+        return;
+
+    if (incomplete>0)
+        return;
+
+    if (dim.state("MCP").name!="TakingData")
+        return;
+
+    console.out("");
+    dim.log("Incomplete event ["+inc+","+incomplete+"] detected, sending MCP/STOP");
+
+    incomplete = inc;
+    dim.send("MCP/STOP");
+}
+
+// ================================================================
+//  Code related to taking data
+// ================================================================
+
+/**
+ * reconnect to problematic FADs
+ *
+ * Dis- and Reconnects to FADs, found to be problematic by call-back function
+ * onchange() to have a different CONNECTION value than 66 or 67. 
+ * 
+ * @returns
+ *      a boolean is returned. 
+ *      reconnect returns true if:
+ *          * nothing needed to be reset --> no problems found by onchange()
+ *          * the reconnection went fine.
+ *      
+ *      reconnect *never returns false* so far.
+ *
+ * @example
+ *      if (!sub_connections.reconnect())
+ *          exit();
+ */
+function reconnect(list, txt)
+{ /*
+    var reset = [ ];
+
+    for (var i=0; i<list.length; i++)
+        {
+            console.out("  FAD %2d".$(list[i])+" lost during "+txt);
+            reset.push(parseInt(list[i]/10));
+        }
+
+    reset = reset.filter(function(elem,pos){return reset.indexOf(elem)==pos;});
+
+    console.out("");
+    console.out("  FADs belong to crate(s): "+reset);
+    console.out("");
+*/
+    console.out("");
+    dim.log("Trying automatic reconnect ["+txt+",n="+list.length+"]...");
+
+    if (list.length>3)
+        throw new Error("Too many boards to be reconnected. Please check what happened.");
+
+    for (var i=0; i<list.length; i++)
+    {
+        console.out("   ...disconnect "+list[i]);
+        dim.send("FAD_CONTROL/DISCONNECT", list[i]);
+    }
+
+    console.out("   ...waiting for 3s");
+    v8.sleep(3000);
+
+    for (var i=0; i<list.length; i++)
+    {
+        console.out("   ...reconnect "+list[i]);
+        dim.send("FAD_CONTROL/CONNECT", list[i]);
+    }
+
+    console.out("   ...waiting for 1s");
+
+    // Wait for one second to bridge possible pending connects
+    v8.sleep(1000);
+
+    console.out("   ...checking connection");
+
+    // Wait for FAD_CONTROL to realize that all boards are connected
+    // FIXME: Wait for '40' boards being connected instead
+    try
+    {
+        dim.wait("FAD_CONTROL", "Connected", 3000);
+    }
+    catch (e)
+    {
+        if (dim.state("FAD_CONTROL").name!="Connecting")
+        {
+            console.out("");
+            console.out(" + FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
+            console.out("");
+            throw e;
+        }
+
+        var crates = [];
+        for (var i=0; i<list.length; i++)
+            crates[list[i]/4] = true;
+
+        include('scripts/crateReset.js');
+        crateReset(crates);
+    }
+
+    // Wait also for MCP to have all boards connected again
+    dim.wait("MCP", "Idle", 3000);
+
+    dim.log("Automatic reconnect successfull.");
+    console.out("");
+}
+
+function takeRun(type, count, time)
+{
+    if (!count)
+        count = -1;
+    if (!time)
+        time = -1;
+
+    var custom = typeof(type)=="function";
+
+    var nextrun = sub_startrun.get().obj['next'];
+    dim.log("Take run %3d".$(nextrun)+": N="+count+" T="+time+"s ["+(custom?"custom":type)+"]");
+
+    // FIXME: Replace by callback?
+    //
+    // DN: I believe instead of waiting for 'TakingData' one could split this
+    // up into two checks with an extra condition:
+    //  if type == 'data':
+    //      wait until ThresholdCalibration starts:
+    //          --> this time should be pretty identical for each run
+    //      if this takes longer than say 3s:
+    //          there might be a problem with one/more FADs
+    //    
+    //      wait until "TakingData":
+    //          --> this seems to take even some minutes sometimes... 
+    //              (might be optimized rather soon, but still in the moment...)
+    //      if this takes way too long: 
+    //          there might be something broken, 
+    //          so maybe a very high time limit is ok here.
+    //          I think there is not much that can go wrong, 
+    //          when the Thr-Calib has already started. Still it might be nice 
+    //          If in the future RateControl is written so to find out that 
+    //          in case the threshold finding algorithm does 
+    //          *not converge as usual*
+    //          it can complain, and in this way give a hint, that the weather
+    //          might be a little bit too bad.
+    //  else:
+    //      wait until "TakingData":
+    //          --> in a non-data run this time should be pretty short again
+    //      if this takes longer than say 3s:
+    //          there might be a problem with one/more FADs
+    //  
+
+    // Use this if you use the rate control to calibrate by rates
+    //if (!dim.wait("MCP", "TakingData", -300000) )
+    //{
+    //    throw new Error("MCP took longer than 5 minutes to start TakingData"+
+    //                    "maybe this idicates a problem with one of the FADs?");
+    //}
+
+    // ================================================================
+    //  Function for Critical voltage
+    // ================================================================
+
+    // INSTALL a watchdog... send FAD_CONTROL/CLOSE_OPEN_FILES
+    // could send MCP/RESET as well but would result in a timeout
+    var callback = dim.onchange['FEEDBACK'];
+    dim.onchange['FEEDBACK'] = function(state)
+    {
+        if (callback)
+            callback.call(this, state);
+
+        if ((state.name=="Critical" || state.name=="OnStandby") &&
+            (this.last!="Critical"  && this.last!="OnStandby"))
+        {
+            console.out("Feedback state changed from "+this.last+" to "+state.name+" [takeRun.js]");
+
+            // Includes FAD_CONTROL/CLOSE_ALL_OPEN_FILES
+            dim.send("MCP/STOP");
+        }
+
+        this.last=state.name;
+    }
+
+    // Here we could check and handle fad losses
+
+    incomplete = 0;
+
+    var start = true;
+
+    for (var n=0; n<3; n++)
+    {
+        if (start)
+        {
+            dim.send("MCP/START", time, count, custom?"custom":type);
+            if (custom)
+                type();
+        }
+
+        try
+        {
+            dim.wait("MCP", "TakingData", 15000);
+            break;
+        }
+        catch (e)
+        {
+            if (dim.state("MCP").name=="TriggerOn" &&
+                dim.state("FAD_CONTROL").name=="Connected" &&
+                dim.state("FTM_CONTROL").name=="TriggerOn")
+            {
+                console.out("");
+                console.out("Waiting for TakingData timed out. Everything looks ok, but file not yet open... waiting once more.");
+                start = false;
+                continue;
+            }
+
+            start = true;
+
+            console.out("");
+            console.out(" + MCP:         "+dim.state("MCP").name);
+            console.out(" + FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
+            console.out(" + FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
+            console.out("");
+
+            if (dim.state("MCP").name!="Configuring3" ||
+                (dim.state("FAD_CONTROL").name!="Configuring1" &&
+                 dim.state("FAD_CONTROL").name!="Configuring2"))
+                throw e;
+
+            console.out("");
+            console.out("Waiting for fadctrl to get configured timed out... checking for in-run FAD loss.");
+
+            var con  = sub_connections.get();
+            var stat = con.obj['status'];
+
+            console.out("Sending MCP/RESET");
+            dim.send("MCP/RESET");
+
+            dim.wait("FTM_CONTROL", "Valid",     3000);
+            dim.wait("FAD_CONTROL", "Connected", 3000);
+            dim.wait("MCP",         "Idle",      3000);
+
+            var list = [];
+            for (var i=0; i<40; i++)
+                if (stat[i]!=0x43)
+                    list.push(i);
+
+            reconnect(list, "configuration");
+
+            if (n==2)
+                throw e;
+
+            //dim.wait("MCP", "Idle", 3000);
+        }
+    }
+
+    // This is to check if we have missed the event. This can happen as
+    // a race condition when the MCP/STOP is sent by the event handler
+    // but the run was not yet fully configured.
+    var statefb = dim.state("FEEDBACK").name;
+    if (statefb=="Critical" || statefb=="OnStandby")
+    {
+        console.out("Run started by FEEDBACK in state "+statefb);
+        dim.send("MCP/STOP"); // Includes FAD_CONTROL/CLOSE_ALL_OPEN_FILES
+
+        dim.onchange['FEEDBACK'] = callback;
+
+        return true;
+    }
+
+    dim.wait("MCP", "Idle", time>0 ? time*1250 : undefined); // run time plus 25%
+
+    // REMOVE watchdog
+    dim.onchange['FEEDBACK'] = callback;
+
+    if (incomplete)
+    {
+        console.out("");
+        console.out(" - MCP:         "+dim.state("MCP").name);
+        console.out(" - FAD_CONTROL: "+dim.state("FAD_CONTROL").name);
+        console.out(" - FTM_CONTROL: "+dim.state("FTM_CONTROL").name);
+
+        dim.wait("FTM_CONTROL", "Valid",     3000);
+        dim.wait("FAD_CONTROL", "Connected", 3000);
+        dim.wait("MCP",         "Idle",      3000);
+
+        var str = incomplete.toString(2);
+        var len = str.length;
+
+        var list = [];
+        for (var i=0; i<str.length; i++)
+            if (str[str.length-i-1]=='1')
+                list.push(i);
+
+        reconnect(list, "data taking");
+
+        return false;
+    }
+
+    // FIXME: What if the ext1 is not enabled in the configuration?
+    if (type=="data")
+    {
+        var dim_trg = new Subscription("FAD_CONTROL/TRIGGER_COUNTER");
+        var counter = dim_trg.get(3000);
+
+        // The check on physics and pedestal triggers is to ensure that
+        // there was at least a chance to receive any event (e.g. in case
+        // of an interrupt this might not be the case)
+        if (counter.qos!=111 &&
+            (counter.data['N_trg']>1000 || counter.data['N_ped']>5) &&
+            counter.data['N_ext1']==0) // 'o' for open
+            throw new Error("No ext1 triggers received during data taking... please check the reason and report in the logbook.");
+        dim_trg.close();
+    }
+
+    return true;
+}
Index: branches/FACT++_scripts_refactoring/tests_n_examples/example_Access_of_a_service.py
===================================================================
--- branches/FACT++_scripts_refactoring/tests_n_examples/example_Access_of_a_service.py	(revision 18221)
+++ branches/FACT++_scripts_refactoring/tests_n_examples/example_Access_of_a_service.py	(revision 18221)
@@ -0,0 +1,107 @@
+'use strict';
+
+// Subscribe to the the services (returns a handle to each of them)
+var w = new Subscription("MAGIC_WEATHER/DATA");
+var x = new Subscription("TNG_WEATHER/DUST");
+var y = new Subscription("TNG_WEATHER/CLIENT_LIST");
+
+// Name which corresponds to handle
+console.out(w.name);
+
+// Wait until a valid service object is in the internal buffer
+while (!w.get(-1))
+    v8.sleep(100);
+
+// Make sure that the service description for this service is available
+// This allows to access the service values by name (access by index
+// is always possible)
+while (!w.get().obj)
+    v8.sleep(100);
+
+console.out("have data");
+
+// get the current service data
+var d = w.get();
+
+// Here is a summary:
+//    d.obj===undefined: no data received yet
+//    d.obj!==undefined, d.obj.length==0: valid names are available, received data empty (d.data===null)
+//    obj!==undefined, obj.length>0: valid names are available, data received
+//
+//    d.data===undefined: no data received yet
+//    d.data===null: event received, but contains no data
+//    d.data.length>0: event received, contains data
+
+console.out("Format: "+d.format); // Dim format string
+console.out("Counter: "+d.counter); // How many service object have been received so far?
+console.out("Time: "+d.time); // Which time is attached to the data?
+console.out("QoS: "+d.qos); // Quality-of-Service parameter
+console.out("Length: "+d.data.length); // Number of entries in data array
+console.out("Data: "+d.data); // Print array
+
+// Or to plot the whole contents, you can do
+console.out(JSON.stringify(d));
+console.out(JSON.stringify(d.data));
+console.out(JSON.stringify(d.obj));
+
+// Loop over all service properties by name
+for (var name in d.obj)
+{
+    console.out("obj." + name + "=" + d.obj[name]);
+}
+
+// Loop over all service properties by index
+for (var i=0; i<d.data.length; i++)
+{
+    console.out("data["+ i +"]="+ d.data[i]);
+}
+
+// Note that in case of formats like F:160, the entries in data
+// might be arrays themselves
+
+var cnt = d.counter;
+
+// Print counter and time
+console.out("Time: "+d.counter+" - "+d.time);
+
+// Wait until at least one new event has been received
+while (cnt==d.counter)
+{
+    v8.sleep(1000);
+    d = w.get();
+}
+
+// Print counter and time of new event (usually counter+1, but this is
+// not guranteed) We can have missed service objects
+console.out("Time: "+d.counter+" - "+d.time);
+
+// Access the Wind property of the weather data
+console.out("Wind: "+d.obj.v);
+console.out("Wind: "+d.obj['v']);
+
+// Get the dust and client_list
+var xx = x.get();
+var yy = y.get();
+
+// data contains only a single value. No array indexing required
+console.out("Dust: "+xx.data);
+console.out("CL:   "+yy.data);
+
+// Service is still open
+console.out(w.isOpen);
+
+// Close the subscription (unsubscribe from the dim-service)
+// Tells you if the service was still subscribed or not
+var rc = w.close();
+
+// Service is not subscribed anymore
+console.out(w.isOpen);
+console.out(rc)
+
+
+rc = x.close();
+console.out(rc)
+rc = y.close();
+console.out(rc)
+
+
Index: branches/FACT++_scripts_refactoring/tests_n_examples/test_Sun.js
===================================================================
--- branches/FACT++_scripts_refactoring/tests_n_examples/test_Sun.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/tests_n_examples/test_Sun.js	(revision 18221)
@@ -0,0 +1,16 @@
+'use strict';
+var date = new Date(); // Date in UTC
+console.out(date);
+console.out("------------------------------");
+console.out(" - no params: -");
+console.out( JSON.stringify( Sun.horizon()  ) );
+console.out('params: ("astro") ');
+console.out( JSON.stringify( Sun.horizon("astro") ) );
+console.out('params: (-12, date) ');
+console.out(JSON.stringify(Sun.horizon(-12, date ) ) ); // nautical
+console.out('params: ("FACT") ');
+console.out(JSON.stringify(Sun.horizon("FACT") ) );
+console.out("------------------------------");
+console.out("-----------time when LidOpen() will work-----------");
+console.out('nautical');
+console.out(JSON.stringify(Sun.horizon("nautical" ) ) ); // nautical
Index: branches/FACT++_scripts_refactoring/tests_n_examples/test_double_subscription.js
===================================================================
--- branches/FACT++_scripts_refactoring/tests_n_examples/test_double_subscription.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/tests_n_examples/test_double_subscription.js	(revision 18221)
@@ -0,0 +1,42 @@
+'use strict';
+
+// Subscribe to the the services (returns a handle to each of them)
+var w = new Subscription("MAGIC_WEATHER/DATA");
+
+// Name which corresponds to handle
+console.out(w.name);
+
+// Wait until a valid service object is in the internal buffer
+while (!w.get(-1))
+    v8.sleep(100);
+
+// Make sure that the service description for this service is available
+// This allows to access the service values by name (access by index
+// is always possible)
+while (!w.get().obj)
+    v8.sleep(100);
+
+console.out("have data");
+
+
+console.out("double subscription");
+var ww = new Subscription("MAGIC_WEATHER/DATA");
+
+console.out("closing 1st subscription.");
+
+var rc = w.close();
+
+console.out(" closing worked? "+rc);
+console.out("is 2nd subscription still open? "+ww.isOpen());
+
+if (ww.isOpen())
+{
+    console.out("was still open?! ... closing it ...");
+    ww.close();
+}
+else
+{
+    console.out("what if we close an already closed subscription?");
+    ww.close();
+}
+
Index: branches/FACT++_scripts_refactoring/tests_n_examples/test_if_FEEDBACK_CALIBRATION_times_out.py
===================================================================
--- branches/FACT++_scripts_refactoring/tests_n_examples/test_if_FEEDBACK_CALIBRATION_times_out.py	(revision 18221)
+++ branches/FACT++_scripts_refactoring/tests_n_examples/test_if_FEEDBACK_CALIBRATION_times_out.py	(revision 18221)
@@ -0,0 +1,13 @@
+'use strict';
+
+// DN 09.12.2012
+
+// this script tests, if FEEDBACK will really time out 
+// when started freshly, when one tries to get the CALIBRATION Service.
+
+// so this script will run fine, when FEEDBACK already had a CALIBRATION in the past
+// and it will throw an exception after the long time of 50sec, when 
+// FEEDBACK had never ever had a CALIBRATION (e.g. becauce it was started just a minute ago)
+var service_calibration = new Subscription("FEEDBACK/CALIBRATION");
+var data_calibration = service_calibration.get(50000);
+
Index: branches/FACT++_scripts_refactoring/tests_n_examples/throw.js
===================================================================
--- branches/FACT++_scripts_refactoring/tests_n_examples/throw.js	(revision 18221)
+++ branches/FACT++_scripts_refactoring/tests_n_examples/throw.js	(revision 18221)
@@ -0,0 +1,2 @@
+console.out("incl");
+throw new Error("test error");
