// detect anomalies of given param data according to given model id.
        // this function is in one task, and might take time.
        // return null if failed.
        public ANOMALY Detect(int idModel, Predict_Data data)
        {
            string fileName = "";
            List <CorrelatedFeatures> correlation = null;

            lock (L_NormalModels)
            {
                // get file name (json file). within our server program deleting files,
                // is only via the beginning of Main or via AnomalyDetectorsManager_instance.Remove(), and only 1 instance exists,
                // but we lock L_NormalModels so it won't be removed meanwhile
                if (L_NormalModels.ContainsKey(idModel) && L_NormalModels[idModel].status == MODEL.Status_Ready)
                {
                    fileName = L_NormalModels[idModel].FileName();
                }
                if (String.IsNullOrWhiteSpace(fileName))
                {
                    return(null);
                }
                // load normal model
                correlation = IO_Util.LoadNormalModel(fileName);
            }
            if (correlation == null)
            {
                return(null);
            }
            try
            {
                // get detection (default detection method = "hybrid" , which it's enough if the learning was with hybrid\regression method)
                var detection = AnomalyDetection.GetDetection(data.predict_data, correlation);
                if (detection == null)
                {
                    return(null);
                }
                Dictionary <string, string>       reason         = AnomalyDetection.GetReportTypes(correlation, detection);
                Dictionary <String, List <Span> > spanDictionary = AnomalyDetection.ToSpanDictionary(detection);
                if (reason == null || spanDictionary == null)
                {
                    return(null);
                }
                return(new ANOMALY()
                {
                    anomalies = spanDictionary, reason = reason
                });
            }catch
            {
                return(null);
            }
        }
        // try to learn new normal model and save it in correspond json file.
        // the learning is in new Task and the return MODEL status field is therfore "pending".
        // does afterFinishingLearning Action after the learning finished.
        public MODEL LearnAndAddNewModel(string detectoionType, Train_Data data, Action afterFinishingLearning)
        {
            MODEL model;
            // random new model_id
            Random rnd = new Random(DateTime.Now.Millisecond);
            int    id  = rnd.Next();

            // lock L_NormalModels in order to add the new mode_id
            lock (L_NormalModels)
            {
                // check it doesn't appear already, otherwise random new id.
                while (L_NormalModels.ContainsKey(id) || System.IO.File.Exists(new MODEL()
                {
                    model_id = id
                }.FileName()))
                {
                    id = rnd.Next();
                }
                model = new MODEL()
                {
                    model_id    = id, status = MODEL.Status_Pending,
                    upload_time = DateTime.Now
                };
                L_NormalModels.Add(id, model);
            }
            // start new task of learning correlative features
            Task.Run(() => {
                try
                {
                    // learn noraml mode [that might take while], only if id not deleted yet
                    var correlation = AnomalyDetection.GetNormal(data.train_data, detectoionType, () => this.IsExist(id));
                    if (correlation == null)
                    {
                        throw new Exception();
                    }
                    // save it to json file
                    bool isSuccess = IO_Util.SaveNormalModel(model.FileName(), correlation,
                                                             new MODEL()
                    {
                        model_id = model.model_id, status = MODEL.Status_Ready, upload_time = model.upload_time
                    });

                    if (!isSuccess)
                    {
                        throw new Exception();
                    }
                    lock (L_NormalModels)
                    {
                        if (L_NormalModels.ContainsKey(id))
                        {
                            L_NormalModels[id].status = MODEL.Status_Ready;
                        }
                        else
                        {
                            // probably we won't get here since correlaion wil be null if id was deleted from L_NormalModels,
                            // because we sent the lambda ()=>this.IsExist(id) to AnomalyDetection.GetNormal
                            try { System.IO.File.Delete(new MODEL()
                                {
                                    model_id = id
                                }.FileName()); }
                            catch { }
                        }
                    }
                } catch {
                    lock (L_NormalModels)
                    {
                        if (L_NormalModels.ContainsKey(id))
                        {
                            L_NormalModels[id].status = MODEL.Status_Corrupted;
                        }
                    }
                }
                afterFinishingLearning();
            });
            // return MODEL even before the learning finished
            // { model_id = id, status = MODEL.Status_Pending, upload_time = DateTime.Now };
            return(model);
        }