Index: /trunk/FACT++/src/scheduler.cc
===================================================================
--- /trunk/FACT++/src/scheduler.cc	(revision 19529)
+++ /trunk/FACT++/src/scheduler.cc	(revision 19530)
@@ -64,4 +64,6 @@
     Nova::SolarObjects solarobj;
     Nova::ZdAzPosn position;
+    Nova::RstTime vis_sun;
+    Nova::RstTime vis_obj;
 
     double moon_dist   = -1;
@@ -75,5 +77,5 @@
     bool valid_threshold = false;
 
-    bool visible       = false;
+    bool visible       = false; // And of all the above except valid_threshold
 
     void calc(const Nova::EquPosn &equ, const double &jd)
@@ -82,4 +84,6 @@
         position  = Nova::GetHrzFromEqu(equ, jd);
         moon_dist = Nova::GetAngularSeparation(equ, solarobj.fMoonEqu);
+        vis_sun   = Nova::GetSolarRst(jd, -12);
+        vis_obj   = Nova::GetObjectRst(equ, jd, zd_max);
 
         current   = FACT::PredictI(solarobj, equ);
@@ -459,12 +463,12 @@
         const string query1 =
             "SELECT\n"
-            "   fSourceKey,\n"
-            "   fSourceName,\n"
-            "   ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist,\n"
-            "   fIsToO\n"
-            " FROM Source\n"
-            " WHERE fSourceTypeKEY=1\n"
-            " HAVING Dist<"+to_string(check_dist)+"\n"
-            " ORDER BY Dist ASC";
+            " fSourceKey,\n"
+            " fSourceName,\n"
+            " ADIST(fDeclination, fRightAscension*15, "+to_string(dec)+", "+to_string(ra)+") AS Dist,\n"
+            " fIsToO\n"
+            "FROM Source\n"
+            "WHERE fSourceTypeKEY=1\n"
+            "HAVING Dist<"+to_string(check_dist)+"\n"
+            "ORDER BY Dist ASC";
 
         const mysqlpp::StoreQueryResult res1 = fConnection.query(query1).store();
@@ -530,5 +534,5 @@
             " ORDER BY fMagnitude ASC";
 
-        Out() << query0 << endl;
+        // Out() << query0 << endl;
 
         const mysqlpp::StoreQueryResult res0 = fConnection.query(query0).store();
@@ -593,6 +597,32 @@
     }
 
-
-    bool ScheduleImp(const string &name, const double &ra, const double &dec, const CheckVisibility &check, const bool &is_pointing)
+    uint32_t AddSource(const string &name, const double &ra, const double &dec, const bool fDryRun=false)
+    {
+        const double wobble_angle = GetWobbleAngle(ra, dec);
+
+        const string query =
+            "INSERT INTO Source\n"
+            " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
+            " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
+
+        if (fDryRun)
+        {
+            Out() << query << endl;
+            return -1;
+        }
+
+        auto q = fConnection.query(query);
+        q.execute();
+        if (!q.info().empty())
+            Info(q.info());
+
+        const uint32_t source_key = q.insert_id();
+
+        Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source_key));
+
+        return source_key;
+    }
+
+    bool ScheduleImp(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
     {
         const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
@@ -627,16 +657,19 @@
          */
 
-        Source source = GetSourceKey(name, ra, dec);
+        Source source = GetSourceKey(name, grb.ra, grb.dec);
 
         Out() << Time()-stopwatch << endl;
 
-        if (is_pointing && source.key<0) // Not a known source
-        {
-            Warn("Observation of only known sources requested but no known source found.");
-            return false;
-        }
-
-        if (source.key>=0)
-        {
+        // This is not a real alert but a pointing information of a satellite
+        if (is_pointing)
+        {
+            // We only observe known sources in paralell with satellites...
+            if (source.key<0)
+            {
+                Warn("Observation of only known sources requested but no known source found.");
+                return false;
+            }
+
+            // ... and only if they are 'standard' sources and not ToO sources.
             if (source.isToO)
             {
@@ -645,4 +678,6 @@
             }
 
+            // For non prioritized sources we require a threshold <3
+            // and for prioritized sources a threshold <10
             const set<uint16_t> prioritized_sources = { 1, 2, 7 };
 
@@ -655,4 +690,5 @@
             }
         }
+
 
         /*
@@ -842,28 +878,5 @@
 
         if (source.key<0)
-        {
-            const double wobble_angle = GetWobbleAngle(ra, dec);
-
-            Out() << Time()-stopwatch << endl;
-
-            const string query =
-                "INSERT INTO Source\n"
-                " (fSourceName, fRightAscension, fDeclination, fWobbleAngle0, fWobbleAngle1, fSourceTypeKey, fIsToO) VALUES\n"
-                " ('"+name+"', "+to_string(ra/15.)+", "+to_string(dec)+", "+to_string(wobble_angle-90)+", "+to_string(wobble_angle+90)+", 1, 1)";
-
-            if (!fDryRun)
-            {
-                auto q = fConnection.query(query);
-                q.execute();
-                if (!q.info().empty())
-                    Info(q.info());
-
-                source.key = q.insert_id();
-
-                Info(string(fDryRun?"[dry-run] ":"")+"The new source got the key "+to_string(source.key));
-            }
-            else
-                Out() << query << endl;
-        }
+            source.key = AddSource(name, grb.ra, grb.dec, fDryRun);
 
         Out() << Time()-stopwatch << endl;
@@ -961,12 +974,31 @@
         Info(out);
 
+        // ---------------------------------------------------------
+
+        const string queryT =
+            "INSERT INTO ToOs\n"
+            " (fTypeID, fRightAscension, fDeclination, fSourceKEY) VALUES\n"
+            " ('"+to_string(grb.type)+"', "+to_string(grb.ra/15)+", "+to_string(grb.dec)+", "+to_string(source.key)+")";
+
+        if (!fDryRun)
+        {
+            auto q = fConnection.query(queryT);
+            q.execute();
+            if (!q.info().empty())
+                Info(q.info());
+        }
+        else
+            Out() << queryT << endl;
+
+        // ---------------------------------------------------------
+
         return true;
     }
-
-    bool Schedule(const string &name, const double &ra, const double &dec, const CheckVisibility &check, const bool &is_pointing)
+/*
+    bool Schedule(const string &name, const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
     {
         try
         {
-            return ScheduleImp(name, ra, dec, check, is_pointing);
+            return ScheduleImp(name, grb, check, is_pointing);
         }
         catch (const exception &e)
@@ -977,5 +1009,5 @@
         }
     }
-
+*/
     // ========================================================================
 
@@ -994,7 +1026,62 @@
     }
 
+    bool CheckMinEventSize(size_t has, const char *name, size_t size)
+    {
+        if (has>size)
+            return true;
+
+        ostringstream msg;
+        msg << name << " - Received event has " << has << " bytes, but expected more.";
+        Fatal(msg);
+        return false;
+    }
+
+    void EnterIntoAlertDB(const ToO::DataGRB &grb, const CheckVisibility &check, const bool &is_pointing)
+    {
+        // Do not enter satellite pointing alerts (as there are too many)
+        if (is_pointing)
+            return;
+
+        // Check for general visibility of the source during the night
+        // If required: For better performance, both (sun and object)
+        // RstTime could be calculated only here
+        const double sun_set  = fmod(check.vis_sun.set,     1);
+        const double sun_rise = fmod(check.vis_sun.rise,    1);
+
+        const double rise     = fmod(check.vis_obj.rise,    1);
+        const double trans    = fmod(check.vis_obj.transit, 1);
+        const double set      = fmod(check.vis_obj.set,     1);
+
+        if ((rise <sun_set || rise >sun_rise) &&
+            (trans<sun_set || trans>sun_rise) &&
+            (set  <sun_set || set  >sun_rise) )
+            return;
+
+        Info("Source might be observable.");
+
+        // Make an entry in the alter databse to issue a call
+        const string queryS =
+            "INSERT FlareAlerts.FlareTriggers SET\n"
+            " fTriggerInserted=Now(),\n"
+            " fNight="+to_string(Time().NightAsInt())+",\n"
+            " fRunID="+to_string(grb.type)+",\n"
+            " fTriggerType=7";
+
+        const bool fDryRun = GetCurrentState()!=ToO::State::kArmed;
+
+        if (!fDryRun)
+        {
+            auto q = fConnection.query(queryS);
+            q.execute();
+            if (!q.info().empty())
+                Info(q.info());
+        }
+        else
+            Out() << queryS << endl;
+    }
+
     int ScheduleGCN(const EventImp &evt)
     {
-        if (!CheckEventSize(evt.GetSize(), "ScheduleGCN", 2+4+3*8))
+        if (!CheckMinEventSize(evt.GetSize(), "ScheduleGCN", sizeof(ToO::DataGRB)))
             return kSM_FatalError;
 
@@ -1002,5 +1089,4 @@
 
         const auto it = GCN::PaketTypes.find(grb.type);
-
         if (it==GCN::PaketTypes.end())
         {
@@ -1009,54 +1095,11 @@
         }
 
+
+        const string name = evt.Ptr<char>(sizeof(ToO::DataGRB));
+
         const auto &paket = it->second;
 
         ostringstream out;
-        out << "Received: '" << paket.name << "' ID=" << grb.type << " NUM=" << grb.trigid << " RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
-
-        /*
-            const std::vector<int16_t> typelist =
-            {
-                51,  // INTEGRAL_POINTDIR
-
-                53,  // INTEGRAL_WAKEUP
-                54,  // INTEGRAL_REFINED
-                55,  // INTEGRAL_OFFLINE
-
-                // 56, // INTEGRAL_WEAK
-                // 59, // KONUS_LC
-
-                60,  // SWIFT_BAT_GRB_ALERT
-                61,  // SWIFT_BAT_GRB_POS_ACK
-                62,  // SWIFT_BAT_GRB_POS_NACK
-
-                83,  // SWIFT_POINTDIR
-
-                97,  // SWIFT_BAT_QL_POS
-
-                100, // AGILE_GRB_WAKEUP
-                101, // AGILE_GRB_GROUND
-                102, // AGILE_GRB_REFINED
-
-                110, // FERMI_GBM_FLT_POS
-                111, // FERMI_GBM_GND_POS
-                112, // FERMI_GBM_LC
-                115, // FERMI_GBM_TRANS
-
-                123, // FERMI_LAT_TRANS
-                125, // FERMI_LAT_MONITOR
-
-                // 134, // MAXI_UNKNOWN
-                // 135, // MAXI_KNOWN
-                // 136, // MAXI_TEST
-
-                157, // AMON_ICECUBE_COINC
-                158, // AMON_ICECUBE_HESE
-
-                169, // AMON_ICECUBE_EHE
-                171, // HAWC_BURST_MONITOR
-                173, // ICECUBE_GOLD
-                174, // ICECUBE_BRONZE
-            };
-         */
+        out << name << " [" << paket.name << ":" << grb.type << "]: RA=" << grb.ra/15. << "h DEC=" << grb.dec << "\u00b0 ERR=" << grb.err;
 
         Info(out);
@@ -1064,6 +1107,4 @@
 
         const CheckVisibility check(grb.ra, grb.dec);
-
-        //Info(string("Source is")+(check.visible?" ":" NOT ")+"visible.");
 
         Info(string("Sun altitude:   ")+(check.valid_sun      ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.solarobj.fSunHrz.alt)+"\u00b0]");
@@ -1073,17 +1114,8 @@
             Info(string("Zenith angle:   ")+(check.valid_zd       ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.position.zd)+"\u00b0]");
             Info(string("Current:        ")+(check.valid_current  ?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.current)+" \u00b5A]");
-            Info(string("Rel. threshold: ")+(check.valid_threshold?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.threshold)+"]");
-        }
-
-        if (!check.visible)
-        {
-            Info("Source not observable... skipped.");
-            return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
-        }
-
-        const string name = paket.instrument+"#"+to_string(grb.trigid);
-        Info("Source is visible... name set to '"+name+"'");
-
-        /*
+            //Info(string("Rel. threshold: ")+(check.valid_threshold?"OK    ":"failed")+" ["+Tools::Form("%5.1f", check.threshold)+"]");
+        }
+
+/*
          * Swift: https://gcn.gsfc.nasa.gov/swift.html
          relevante Paket-Typen:
@@ -1149,8 +1181,59 @@
         const bool is_pointing = grb.type==51 || grb.type==83;
 
-        Schedule(name, grb.ra, grb.dec, check, is_pointing);
+        try
+        {
+            if (!check.visible)
+            {
+                Info("Source not observable... skipped.");
+                EnterIntoAlertDB(grb, check, is_pointing);
+
+                return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
+            }
+
+            Info("Source is visible... scheduling.");
+
+            if (!ScheduleImp(name, grb, check, is_pointing))
+                EnterIntoAlertDB(grb, check, is_pointing);
+        }
+        catch (const exception &e)
+        {
+            Error(string("SQL query failed: ")+e.what());
+            fConnection.disconnect();
+        }
 
         return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
-        //return GetCurrentState();
+    }
+
+    int AddNewSource(const EventImp &evt)
+    {
+        if (!CheckMinEventSize(evt.GetSize(), "AddNewSource", 2*sizeof(double)))
+            return kSM_FatalError;
+
+        const double &ra  = evt.Ptr<double>()[0];
+        const double &dec = evt.Ptr<double>()[1];
+        const string name = evt.Ptr<char>(sizeof(double)*2);
+
+        ostringstream out;
+        out << "New Source: '" << name << "' RA=" << ra << "h DEC=" << dec << "\u00b0";
+
+        Info(out);
+
+        try
+        {
+            const Source source = GetSourceKey(name, ra*15, dec);
+
+            if (source.key>=0) // Not a known source
+                Warn("Source already known with key="+to_string(source.key)+".");
+            else
+                /*source.key =*/ AddSource(name, ra*15, dec);
+
+        }
+        catch (const exception &e)
+        {
+            Error(string("SQL query failed: ")+e.what());
+            fConnection.disconnect();
+        }
+
+        return fConnection.connected() ? GetCurrentState() : ToO::State::kDisconnected;
     }
 
@@ -1192,5 +1275,5 @@
 
         if (fConnection.connected() && GetCurrentState()<=ToO::State::kDisconnected)
-            return ToO::State::kConnected;
+            return ToO::State::kArmed;
 
         return GetCurrentState();
@@ -1210,12 +1293,19 @@
                      "All needed subsystems are connected to their hardware, scheduling in progress.");
 
-        AddEvent("GCN", "S:1;I:1;D:1;D:1;D:1")
+        AddEvent("GCN", "S:1;D:1;D:1;D:1;C")
             (bind(&StateMachineToO::ScheduleGCN, this, placeholders::_1))
-            (""
-             "|type[int16]:TypeID (see HeaderGCN.h)"
-             "|trig[uint32]:TriggerID"
+            ("Schedule a ToO"
+             "|type[int16]:Pakage or type ID (see HeadersToO.h)"
              "|ra[deg]:Right ascension"
              "|dec[deg]:Declination"
-             "|err[deg]:Error radius");
+             "|err[deg]:Error radius"
+             "|name[string]:Tentative or known source name");
+
+        AddEvent("ADD_SOURCE", "D:1;D:1;C")
+            (bind(&StateMachineToO::AddNewSource, this, placeholders::_1))
+            ("Add a new source to the databse if not yet available"
+             "|ra[h]:Right ascension"
+             "|dec[deg]:Declination"
+             "|name[string]:Monitored source name");
 
         AddEvent("START", "")
