Пример #1
0
        private void Refresh()
        {
            //in case of disabled cameras:
            if (comboBox_filter_camera.Text != "All Cameras" && comboBox_filter_camera.Text.Trim().ToLower().Trim() != this.cam.name.Trim().ToLower())
            {
                this.cam = AITOOL.GetCamera(comboBox_filter_camera.Text);
            }

            List <ObjectPosition> hist   = new List <ObjectPosition>();
            List <ObjectPosition> masked = new List <ObjectPosition>();

            if (comboBox_filter_camera.Text == "All Cameras")
            {
                foreach (Camera curcam in AppSettings.Settings.CameraList)
                {
                    hist.AddRange(curcam.maskManager.last_positions_history);
                    masked.AddRange(curcam.maskManager.masked_positions);
                }
            }
            else
            {
                hist   = cam.maskManager.last_positions_history;
                masked = cam.maskManager.masked_positions;
            }

            Global_GUI.UpdateFOLV(ref FOLV_MaskHistory, hist, true);
            Global_GUI.UpdateFOLV(ref FOLV_Masks, masked, true);
            this.CurObjPosLst.Clear();
            ShowMaskImage();
            ShowImageMask(null);
        }
        public List <ClsRelevantObject> GetDefaultObjectList(bool Clear)
        {
            List <ClsRelevantObject> ret = this._DefaultObjectsList;

            //get the default camera list

            try
            {
                if (this.defaultcam.IsNull())
                {
                    this.defaultcam = AITOOL.GetCamera("Default", true);
                }

                if (!this.defaultcam.IsNull())  //probably here to soon
                {
                    if (this.defaultcam.DefaultTriggeringObjects.ObjectList.Count > 0 && !this.Camera.EqualsIgnoreCase(this.defaultcam.Name))
                    {
                        this._DefaultObjectsList = this.defaultcam.DefaultTriggeringObjects.CloneObjectList();
                    }
                    else
                    {
                        this._DefaultObjectsList = this.FromString(AppSettings.Settings.ObjectPriority, Clear, false);
                    }

                    ret = this._DefaultObjectsList;
                }
            }
            catch (Exception ex)
            {
                AITOOL.Log($"Error: ({this.TypeName}) {ex.Msg()}");
            }

            return(ret);
        }
Пример #3
0
        private void Refresh()
        {
            //in case of disabled cameras:
            if (!string.Equals(this.comboBox_filter_camera.Text, "All Cameras", StringComparison.OrdinalIgnoreCase) && this.comboBox_filter_camera.Text.ToLower().Trim() != this.cam.Name.ToLower().Trim())
            {
                this.cam = AITOOL.GetCamera(this.comboBox_filter_camera.Text);
            }

            List <ObjectPosition> hist   = new List <ObjectPosition>();
            List <ObjectPosition> masked = new List <ObjectPosition>();

            if (this.comboBox_filter_camera.Text == "All Cameras")
            {
                foreach (Camera curcam in AppSettings.Settings.CameraList)
                {
                    hist.AddRange(curcam.maskManager.LastPositionsHistory);
                    masked.AddRange(curcam.maskManager.MaskedPositions);
                }
            }
            else
            {
                hist   = this.cam.maskManager.LastPositionsHistory;
                masked = this.cam.maskManager.MaskedPositions;
            }

            Global_GUI.UpdateFOLV(this.FOLV_MaskHistory, hist, FullRefresh: true);
            Global_GUI.UpdateFOLV(this.FOLV_Masks, masked, FullRefresh: true);
            this.CurObjPosLst.Clear();
            this.ShowMaskImage();
            this.ShowImageMask(null);
        }
Пример #4
0
        public void ShowCamera()
        {
            if (cmb_cameras.Text.Trim().EqualsIgnoreCase("all cameras"))
            {
                this.CurrentCam = AITOOL.GetCamera("default", true);
            }
            else
            {
                this.CurrentCam = AITOOL.GetCamera(cmb_cameras.Text.Trim());
            }

            cb_paused.Checked = this.CurrentCam.Paused;
            if (this.CurrentCam.Paused)
            {
                cb_paused.Text = "UN-pause";
            }
            else
            {
                cb_paused.Text = "Pause";
            }

            tb_minutes.Text           = this.CurrentCam.PauseMinutes.ToString();
            cb_FileMonitoring.Checked = this.CurrentCam.PauseFileMon;
            cb_MQTT.Checked           = this.CurrentCam.PauseMQTT;
            cb_Pushover.Checked       = this.CurrentCam.PausePushover;
            cb_Telegram.Checked       = this.CurrentCam.PauseTelegram;
            cb_URL.Checked            = this.CurrentCam.PauseURL;

            UpdateButton();
        }
Пример #5
0
 private void FOLV_MaskHistory_CellRightClick(object sender, BrightIdeasSoftware.CellRightClickEventArgs e)
 {
     if (e.Model != null)
     {
         this.contextMenuPosObj = (ObjectPosition)e.Model;
         this.cam = AITOOL.GetCamera(this.contextMenuPosObj.CameraName);
     }
 }
        public void Init(Camera cam = null)
        {
            if (!cam.IsNull())
            {
                this.cam = cam;
            }

            if (this.cam.IsNull() && this.CameraName.IsNotEmpty())
            {
                this.cam = AITOOL.GetCamera(this.CameraName);
            }

            if (!this.cam.IsNull())
            {
                this.CameraName = this.cam.Name;
            }

            //dont initialize until we have a list of cameras available
            if (!this.Initialized || !this.ObjectDict.IsNull() && !AppSettings.Settings.CameraList.IsNull() && AppSettings.Settings.CameraList.Count > 0)
            {
                lock (ROLockObject)
                {
                    //migrate from the dictionary to the list - dictionary no longer used
                    if (!this.ObjectDict.IsNull())
                    {
                        if (this.ObjectDict.Count > 0)
                        {
                            ObjectList.Clear();
                            foreach (Object item in ObjectDict.Values)
                            {
                                if (item is DictionaryEntry)
                                {
                                    ClsRelevantObject ro = (ClsRelevantObject)((DictionaryEntry)item).Value;
                                    ObjectList.Add(ro.CloneJson());
                                }
                                else if (item is ClsRelevantObject)
                                {
                                    ClsRelevantObject ro = (ClsRelevantObject)item;
                                    ObjectList.Add(ro.CloneJson());
                                }
                                else
                                {
                                    AITOOL.Log($"Warn: Old object is {item.GetType().FullName}??");
                                }
                            }
                        }

                        this.ObjectDict = null;
                    }

                    //Add default settings
                    this.Initialized = true;

                    Update();
                }
            }
        }
Пример #7
0
        private void comboBox_filter_camera_SelectionChangeCommitted(object sender, EventArgs e)
        {
            //I think this event only triggers when user picks something NOT when items are initially added to combobox
            if (!(this.comboBox_filter_camera.Text == "All Cameras"))
            {
                this.BtnDynamicMaskingSettings.Enabled = true;
                this.cam = AITOOL.GetCamera(this.comboBox_filter_camera.Text);
            }
            else
            {
                this.BtnDynamicMaskingSettings.Enabled = false;
            }

            this.Refresh();
        }
Пример #8
0
        private void FOLV_MaskHistory_SelectionChanged(object sender, EventArgs e)
        {
            this.CurObjPosLst.Clear();

            if (this.FOLV_MaskHistory.SelectedObjects != null && this.FOLV_MaskHistory.SelectedObjects.Count > 0)
            {
                this.contextMenuPosObj = (ObjectPosition)this.FOLV_MaskHistory.SelectedObjects[0];
                this.cam = AITOOL.GetCamera(this.contextMenuPosObj.CameraName);

                this.ShowMaskImage();

                foreach (object obj in this.FOLV_MaskHistory.SelectedObjects)
                {
                    this.CurObjPosLst.Add((ObjectPosition)obj);
                }
            }
            this.pictureBox1.Refresh();
        }
Пример #9
0
        private void FOLV_Masks_SelectionChanged(object sender, EventArgs e)
        {
            this.CurObjPosLst.Clear();

            if (FOLV_Masks.SelectedObjects != null && FOLV_Masks.SelectedObjects.Count > 0)
            {
                contextMenuPosObj = (ObjectPosition)FOLV_Masks.SelectedObject;
                this.cam          = AITOOL.GetCamera(contextMenuPosObj.cameraName);

                ShowMaskImage();

                foreach (object obj in FOLV_Masks.SelectedObjects)
                {
                    CurObjPosLst.Add((ObjectPosition)obj);
                }
            }
            pictureBox1.Refresh();
        }
Пример #10
0
        public override string ToString()
        {
            //LabelDisplayFormat = "[Detection] [[Detail]] ({0:0}%)"

            if (this._cam == null)
            {
                this._cam = AITOOL.GetCamera(this.Camera);
            }

            //Dont call this or we get a stack overflow!
            //string ret = AITOOL.ReplaceParams(this._cam, null, this._curimg, this._cam.DetectionDisplayFormat, this);

            string ret = this._cam.DetectionDisplayFormat;

            ret = Global.ReplaceCaseInsensitive(ret, "[label]", this.Label); //only gives first detection
            ret = Global.ReplaceCaseInsensitive(ret, "[detail]", this.Detail);
            ret = Global.ReplaceCaseInsensitive(ret, "[result]", this.Result.ToString());
            ret = Global.ReplaceCaseInsensitive(ret, "[position]", this.PositionString());
            ret = Global.ReplaceCaseInsensitive(ret, "[confidence]", this.ConfidenceString());
            //if there was no detail string, clean up:
            ret = ret.Replace("[]", "").Replace("()", "").Replace("   ", " ").Replace("  ", " ");

            return(ret);
        }
        public ResultType IsRelevant(List <ClsPrediction> preds, bool IsNew, out bool IgnoreMask, string DbgDetail = "")
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            ResultType ret = ResultType.UnwantedObject;

            IgnoreMask = false;

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }


            //if nothing is 'enabled' assume everything should be let through to be on the safe side  (As if they passed an empty list)
            if (this.ObjectList.Count == 0 || this.EnabledCount == 0)  //assume if no list is provided to always return relevant
            {
                return(ResultType.Relevant);
            }

            //if fred is found, the whole prediction will be ignored
            //triggering_objects = person, car, -FRED
            //found objects = person, fred

            if (preds != null && preds.Count > 0)
            {
                //find at least one thing in the triggered objects list in order to send
                string notrelevant = "";
                string relevant    = "";
                string ignored     = "";
                string notenabled  = "";
                string nottime     = "";
                string nothreshold = "";
                bool   ignore      = false;

                foreach (ClsPrediction pred in preds)
                {
                    string label = pred.Label;


                    if (pred.Result == ResultType.Relevant || IsNew)
                    {
                        ClsRelevantObject ro = this.Get(label, AllowEverything: true, out int FoundIDX);

                        if (!ro.IsNull())
                        {
                            if (ro.Enabled)
                            {
                                if (Global.IsTimeBetween(DateTime.Now, ro.ActiveTimeRange))
                                {
                                    //assume if confidence is 0 it has not been set yet (dynamic masking routine, etc)
                                    if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower && pred.Confidence.Round() <= ro.Threshold_upper)
                                    {
                                        ro.LastHitTime = DateTime.Now;
                                        ro.Hits++;
                                        if (!ro.Trigger)
                                        {
                                            ignore = true;
                                            if (!ignored.Contains(label))
                                            {
                                                ignored += label + ",";
                                            }
                                        }
                                        else
                                        {
                                            ret        = ResultType.Relevant;
                                            IgnoreMask = ro.IgnoreMask;
                                            if (!relevant.Contains(label))
                                            {
                                                relevant += label + ",";
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (!nothreshold.Contains(label))
                                        {
                                            nothreshold += label + $" ({pred.Confidence.Round()}%),";
                                        }
                                    }
                                }
                                else
                                {
                                    if (!nottime.Contains(label))
                                    {
                                        nottime += label + ",";
                                    }
                                }
                            }
                            else
                            {
                                if (!notenabled.Contains(label))
                                {
                                    notenabled += label + ",";
                                }
                            }
                        }
                        else
                        {
                            if (!notrelevant.Contains(label))
                            {
                                notrelevant += label + ",";
                            }
                        }
                    }
                    else
                    {
                        if (!notrelevant.Contains(label))
                        {
                            notrelevant += label + ",";
                        }
                    }
                }

                //Add to the main list
                if (this.cam == null)
                {
                    this.cam = AITOOL.GetCamera(this.Camera);
                }

                if (this.defaultcam == null)
                {
                    this.defaultcam = AITOOL.GetCamera("Default", true);
                }

                //always try to add the current prediction to the list (disabled) to give them the option of enabling later
                foreach (ClsPrediction pred in preds)
                {
                    this.TryAdd(pred.Label, false, out int AddedIDX);


                    //add it to the Current camera list (disabled)
                    this.cam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX2);

                    //add it to the default camera list (disabled)
                    if (!this.defaultcam.IsNull())
                    {
                        this.defaultcam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX3);
                    }
                }


                if (ignore)
                {
                    ret = ResultType.IgnoredObject;
                }

                if (!DbgDetail.IsEmpty())
                {
                    DbgDetail = $" ({DbgDetail})";
                }

                if (relevant.IsEmpty())
                {
                    relevant = "(NONE)";
                }

                if (notrelevant.IsEmpty())
                {
                    notrelevant = "(NONE)";
                }

                if (ignored.IsEmpty())
                {
                    ignored = "(NONE)";
                }

                if (notenabled.IsEmpty())
                {
                    notenabled = "(NONE)";
                }

                if (nottime.IsEmpty())
                {
                    nottime = "(NONE)";
                }

                if (nothreshold.IsEmpty())
                {
                    nothreshold = "(NONE)";
                }

                string maskignore = "";
                if (IgnoreMask)
                {
                    maskignore = " (Mask will be ignored)";
                }

                if (ret != ResultType.Relevant)
                {
                    AITOOL.Log($"Trace: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because objects were not defined to trigger, or were set to ignore: Relevant='{relevant.Trim(", ".ToCharArray())}', Irrelevant='{notrelevant.Trim(", ".ToCharArray())}', Caused ignore='{ignored.Trim(", ".ToCharArray())}', Not Enabled={notenabled.Trim(" ,".ToCharArray())}, Not Time={nottime.Trim(" ,".ToCharArray())}, No Threshold Match={nothreshold.Trim(" ,".ToCharArray())}  All Triggering Objects='{this.ToString()}', {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                }
                else
                {
                    AITOOL.Log($"Trace: RelevantObjectManager: Object is valid for '{this.TypeName}{DbgDetail}' because object(s) '{relevant.Trim(", ".ToCharArray())}' were in trigger objects list '{this.ToString()}',{maskignore} Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                }
            }

            return(ret);
        }
Пример #12
0
        private async void btTest_Click(object sender, EventArgs e)
        {
            this.UpdateURL();

            AITOOL.UpdateAIURLs();

            string pth = Global.GetRegSetting("TestImage", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestImage.jpg"));

            OpenFileDialog ofd = new OpenFileDialog
            {
                InitialDirectory = Path.GetDirectoryName(pth),
                FileName         = Path.GetFileName(pth),
                Title            = "Select test image",

                CheckFileExists = true,
                CheckPathExists = true,

                DefaultExt       = "jpg",
                Filter           = "jpg files (*.jpg)|*.jpg",
                FilterIndex      = 2,
                RestoreDirectory = true,
                ShowReadOnly     = true
            };

            if (ofd.ShowDialog() == DialogResult.OK)
            {
                pth = ofd.FileName;
                Global.SaveRegSetting("TestImage", pth);

                if (File.Exists(pth))
                {
                    //must create a temp unique file every time because database key is the filename
                    Camera cam = AITOOL.GetCamera("default", true);
                    if (cam == null)
                    {
                        cam = new Camera("TEST_CAM");
                    }

                    string ext  = Path.GetExtension(pth);
                    string tpth = Path.Combine(Path.GetTempPath(), $"{cam.Name}.URLTEST.{DateTime.Now:yyyy-MM-dd_HH-mm-ss-fff}{ext}");
                    File.Copy(pth, tpth, true);

                    btTest.Enabled  = false;
                    bt_Save.Enabled = false;
                    btTest.Text     = "Working...";
                    this.UpdateURL();
                    ClsImageQueueItem CurImg = new ClsImageQueueItem(tpth, 0);

                    List <ClsURLItem> linked = new List <ClsURLItem> {
                        this.CurURL
                    };

                    if (this.CurURL.LinkServerResults && !string.IsNullOrEmpty(this.CurURL.LinkedResultsServerList))
                    {
                        linked.AddRange(await AITOOL.WaitForNextURL(cam, false, null, this.CurURL.LinkedResultsServerList));
                    }
                    if (linked.Count > 1)
                    {
                        AITOOL.Log($"Debug: ---- Found '{linked.Count}' linked AI URL's.");
                    }

                    AITOOL.DetectObjectsResult result = await AITOOL.DetectObjects(CurImg, linked, cam);

                    //make sure not stuck in use for the test:
                    foreach (var url in result.OutURLs)
                    {
                        url.InUse.WriteFullFence(false);
                    }

                    btTest.Enabled  = true;
                    bt_Save.Enabled = true;
                    btTest.Text     = "Test";
                    if (result.Success)
                    {
                        Frm_ObjectDetail frm = new Frm_ObjectDetail();
                        frm.PredictionObjectDetails = result.OutPredictions;
                        frm.ImageFileName           = tpth;
                        frm.Show();

                        MessageBox.Show($"Success! {this.CurURL.LastResultMessage}", "Success");
                    }
                    else
                    {
                        MessageBox.Show($"Error! {this.CurURL.LastResultMessage}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                    this.CurURL.ErrCount.WriteUnfenced(0);
                    this.CurURL.CurErrCount.WriteUnfenced(0);
                }
                else
                {
                    MessageBox.Show($"Test file does not exist:\r\n{pth}");
                }
            }
        }
Пример #13
0
        public ClsLogItm Log(string Detail, string AIServer = "", string Camera = "", string Image = "", string Source = "", int Depth = 0, LogLevel Level = null, Nullable <DateTime> Time = default(DateTime?), [CallerMemberName()] string memberName = null)
        {
            if (!this.Enabled)
            {
                return(null);
            }

            lock (this._LockObj)
            {
                this._LastIDX.AtomicIncrementAndGet();
                this.LastLogItm          = new ClsLogItm();
                this.LastLogItm.Detail   = Detail.Replace("|", ";");
                this.LastLogItm.Filename = Path.GetFileName(this._Filename);

                this.LastLogItm.ThreadID = Thread.CurrentThread.ManagedThreadId;

                if (Source == null || string.IsNullOrWhiteSpace(Source))
                {
                    this.LastLogItm.Source = this._LastSource;
                }
                else
                {
                    this.LastLogItm.Source = Source;
                }

                if (Camera == null || string.IsNullOrWhiteSpace(Camera))
                {
                    this.LastLogItm.Camera = this._LastCamera;
                }
                else
                {
                    this.LastLogItm.Camera = Camera;
                    if (!Camera.StartsWith("Trace-"))
                    {
                        this._LastCamera = Camera;
                    }
                }

                if (AIServer == null || string.IsNullOrWhiteSpace(AIServer))
                {
                    this.LastLogItm.AIServer = this._LastAIServer;
                }
                else
                {
                    this.LastLogItm.AIServer = AIServer;
                    if (!AIServer.StartsWith("Trace-"))
                    {
                        this._LastAIServer = AIServer;
                    }
                }

                if (Image == null || string.IsNullOrWhiteSpace(Image))
                {
                    this.LastLogItm.Image = this._LastImage;
                }
                else
                {
                    this.LastLogItm.Image = Path.GetFileName(Image);
                }

                if (Time.HasValue)
                {
                    this.LastLogItm.Date = Time.Value;
                }
                else
                {
                    this.LastLogItm.Date = DateTime.Now;
                }

                if (memberName == ".ctor")
                {
                    memberName = "Constructor";
                }

                this.LastLogItm.Func = memberName.Replace("AITool.", "");

                //deepstack messages spam us...
                bool DeepstackDebug = !string.Equals(this.LastLogItm.Func, "handleredisprocmsg", StringComparison.OrdinalIgnoreCase) || string.Equals(this.LastLogItm.Func, "handleredisprocmsg", StringComparison.OrdinalIgnoreCase) && AppSettings.Settings.deepstack_debug;

                if (!DeepstackDebug)
                {
                    return(this.LastLogItm);
                }

                if (Level == null)
                {
                    if (this.LastLogItm.Detail.IndexOf("fatal:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Fatal;
                    }
                    else if (this.LastLogItm.Detail.IndexOf("error:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Error;
                    }
                    else if (this.LastLogItm.Detail.IndexOf("warning:", StringComparison.OrdinalIgnoreCase) >= 0 || this.LastLogItm.Detail.IndexOf("warn:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Warn;
                    }
                    else if (this.LastLogItm.Detail.IndexOf("info:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Info;
                    }
                    else if (this.LastLogItm.Detail.IndexOf("debug:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Debug;
                    }
                    else if (this.LastLogItm.Detail.IndexOf("trace:", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        Level = LogLevel.Trace;
                    }
                    else
                    {
                        Level = LogLevel.Info;
                    }
                }

                bool HasError = false;

                //remove tags
                if (Level == LogLevel.Error)
                {
                    this.ErrorCount.AtomicIncrementAndGet();
                    HasError = true;
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "error:", "");
                }
                else if (Level == LogLevel.Fatal)
                {
                    this.ErrorCount.AtomicIncrementAndGet();
                    HasError = true;
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "fatal:", "");
                }
                else if (Level == LogLevel.Warn)
                {
                    this.ErrorCount.AtomicIncrementAndGet();
                    HasError = true;
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "warn:", "");
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "warning:", "");
                }
                else if (Level == LogLevel.Info)
                {
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "info:", "");
                }
                else if (Level == LogLevel.Trace)
                {
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "trace:", "");
                }
                else if (Level == LogLevel.Debug)
                {
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "debug:", "");
                }

                if (this.LastLogItm.Detail.TrimStart().StartsWith("{"))
                {
                    this.LastLogItm.Color = this.LastLogItm.Detail.GetWord("{", "}");
                    //strip out the color definition
                    this.LastLogItm.Detail = Global.ReplaceCaseInsensitive(this.LastLogItm.Detail, "{" + this.LastLogItm.Color + "}", "");
                }

                //clean out any whitespace
                //this.LastLogItm.Detail = this.LastLogItm.Detail.TrimStart();

                if (this._CurDepth.ReadFullFence() + Depth > 0)
                {
                    this.LastLogItm.Detail = new string(' ', (this._CurDepth.ReadFullFence() + Depth * 2)) + this.LastLogItm.Detail;
                }

                this.LastLogItm.Depth = this._CurDepth.ReadFullFence() + Depth;
                this.LastLogItm.Level = Level;
                this.LastLogItm.Idx   = this._LastIDX.ReadFullFence();


                if (this._Store)
                {
                    if (Level >= this.MinLevel)
                    {
                        this.Values.Add(this.LastLogItm);
                        this.RecentlyAdded.Enqueue(this.LastLogItm);
                        //keep the log list size down
                        if (this.Values.Count > this.MaxGUILogItems)
                        {
                            this.RecentlyDeleted.Enqueue(this.Values[0]);
                            this.Values.RemoveAt(0);
                        }
                    }
                }

                this.NLogFileWriter.Log(Level, this.LastLogItm.ToString());

                if (Debugger.IsAttached)
                {
                    Console.WriteLine(this.LastLogItm.ToDetailString());
                }

                string itm = this.LastLogItm.ToString();

                //Send telegram error message
                if (AppSettings.Settings.send_telegram_errors &&
                    (HasError) &&
                    AppSettings.Settings.telegram_chatids.Count > 0 &&
                    AITOOL.TriggerActionQueue != null &&
                    !(itm.IndexOf("telegram", StringComparison.OrdinalIgnoreCase) >= 0) &&
                    !(itm.IndexOf("addtriggeraction", StringComparison.OrdinalIgnoreCase) >= 0))
                {
                    //await TelegramText($"[{time}]: {text}"); //upload text to Telegram
                    Camera cam = AITOOL.GetCamera(this._LastCamera);
                    AITOOL.TriggerActionQueue.AddTriggerActionAsync(TriggerType.TelegramText, cam, null, null, true, false, null, this.LastLogItm.ToDetailString());
                }

                //Send pushover error message
                if (AppSettings.Settings.send_pushover_errors &&
                    (HasError) &&
                    !string.IsNullOrEmpty(AppSettings.Settings.pushover_APIKey) &&
                    !string.IsNullOrEmpty(AppSettings.Settings.pushover_UserKey) &&
                    AITOOL.TriggerActionQueue != null &&
                    !(itm.IndexOf("pushover", StringComparison.OrdinalIgnoreCase) >= 0) &&
                    !(itm.IndexOf("addtriggeraction", StringComparison.OrdinalIgnoreCase) >= 0))
                {
                    Camera cam = AITOOL.GetCamera(this._LastCamera);
                    AITOOL.TriggerActionQueue.AddTriggerActionAsync(TriggerType.Pushover, cam, null, null, true, false, null, this.LastLogItm.ToDetailString());
                }

                if (HasError)
                {
                    NLog.LogManager.Flush();
                }
            }

            return(this.LastLogItm);
        }
        public ResultType IsRelevant(List <ClsPrediction> preds, bool IsNew, out bool IgnoreImageMask, out bool IgnoreDynamicMask, string DbgDetail = "")
        {
            using var Trace = new Trace();  //This c# 8.0 using feature will auto dispose when the function is done.

            ResultType ret = ResultType.UnwantedObject;

            IgnoreImageMask   = false;
            IgnoreDynamicMask = false;

            this.Init();
            if (!this.Initialized)
            {
                return(ret);
            }


            //if nothing is 'enabled' assume everything should be let through to be on the safe side  (As if they passed an empty list)
            if (this.ObjectList.Count == 0 || this.EnabledCount == 0)  //assume if no list is provided to always return relevant
            {
                AITOOL.Log($"Debug: RelevantObjectManager: Allowing '{this.TypeName}{DbgDetail}' because no relevant objects were enabled. {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}");
                return(ResultType.Relevant);
            }

            //if fred is found, the whole prediction will be ignored
            //triggering_objects = person, car, -FRED
            //found objects = person, fred

            if (preds != null && preds.Count > 0)
            {
                //find at least one thing in the triggered objects list in order to send
                string notrelevant      = "";
                string relevant         = "";
                string ignored          = "";
                string unknownobject    = "";
                string notenabled       = "";
                string nottime          = "";
                string nothreshold      = "";
                string percentsizewrong = "";
                bool   ignore           = false;

                foreach (ClsPrediction pred in preds)
                {
                    string label = pred.Label;


                    if (pred.Result == ResultType.Relevant || IsNew)
                    {
                        ClsRelevantObject ro = this.Get(label, AllowEverything: true, out int FoundIDX);

                        if (!ro.IsNull())
                        {
                            //Add the PercentOfImage size stats in any case:
                            if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower)
                            {
                                ro.PercentSizeStats.AddToCalc(pred.PercentOfImage);
                                ro.HeightStats.AddToCalc(pred.RectHeight);
                                ro.WidthStats.AddToCalc(pred.RectWidth);
                            }

                            if (ro.Enabled)
                            {
                                if (Global.IsTimeBetween(DateTime.Now, ro.ActiveTimeRange))
                                {
                                    //assume if confidence is 0 it has not been set yet (dynamic masking routine, etc)
                                    if (pred.Confidence == 0 || pred.Confidence.Round() >= ro.Threshold_lower && pred.Confidence.Round() <= ro.Threshold_upper)
                                    {
                                        if (pred.PercentOfImage.Round() >= ro.PredSizeMinPercentOfImage && pred.PercentOfImage.Round() <= ro.PredSizeMaxPercentOfImage)
                                        {
                                            ro.LastHitTime = DateTime.Now;
                                            ro.Hits++;
                                            if (!ro.Trigger)
                                            {
                                                ignore = true;
                                                if (!ignored.Contains(label))
                                                {
                                                    ignored += label + ",";
                                                }
                                            }
                                            else
                                            {
                                                ret = ResultType.Relevant;
                                                pred.ObjectResult = ResultType.Relevant;
                                                IgnoreImageMask   = ro.IgnoreImageMask;
                                                if (ro.IgnoreDynamicMask.HasValue)
                                                {
                                                    IgnoreDynamicMask = ro.IgnoreDynamicMask.Value;
                                                }
                                                if (!relevant.Contains(label))
                                                {
                                                    relevant += label + ",";
                                                }
                                            }
                                        }
                                        else
                                        {
                                            if (pred.Confidence.Round() < ro.Threshold_lower)
                                            {
                                                pred.ObjectResult = ResultType.TooSmallPercent;
                                            }
                                            else if (pred.Confidence.Round() > ro.Threshold_upper)
                                            {
                                                pred.ObjectResult = ResultType.TooLargePercent;
                                            }

                                            if (!percentsizewrong.Contains(label))
                                            {
                                                percentsizewrong += label + $" ({pred.PercentOfImage.Round()}%),";
                                            }
                                        }
                                    }
                                    else
                                    {
                                        pred.ObjectResult = ResultType.NoConfidence;
                                        if (!nothreshold.Contains(label))
                                        {
                                            nothreshold += label + $" ({pred.Confidence.Round()}%),";
                                        }
                                    }
                                }
                                else
                                {
                                    pred.ObjectResult = ResultType.NotInTimeRange;
                                    if (!nottime.Contains(label))
                                    {
                                        nottime += label + ",";
                                    }
                                }
                            }
                            else
                            {
                                pred.ObjectResult = ResultType.NotEnabled;
                                if (!notenabled.Contains(label))
                                {
                                    notenabled += label + ",";
                                }
                            }
                        }
                        else
                        {
                            pred.ObjectResult = ResultType.UnknownObject;
                            if (!unknownobject.Contains(label))
                            {
                                unknownobject += label + ",";
                            }
                        }
                    }
                    else
                    {
                        pred.ObjectResult = pred.Result;
                        if (!notrelevant.Contains(label))
                        {
                            notrelevant += label + ",";
                        }
                    }
                }

                //Add to the main list
                if (this.cam == null)
                {
                    this.cam = AITOOL.GetCamera(this.CameraName);
                }

                if (this.defaultcam == null)
                {
                    this.defaultcam = AITOOL.GetCamera("Default", true);
                }

                //always try to add the current prediction to the list (disabled) to give them the option of enabling later
                foreach (ClsPrediction pred in preds)
                {
                    this.TryAdd(pred.Label, false, out int AddedIDX);


                    //add it to the Current camera list (disabled)
                    this.cam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX2);

                    //add it to the default camera list (disabled)
                    if (!this.defaultcam.IsNull())
                    {
                        this.defaultcam.DefaultTriggeringObjects.TryAdd(pred.Label, false, out int AddedIDX3);
                    }
                }


                if (ignore)
                {
                    ret = ResultType.IgnoredObject;
                }

                if (!DbgDetail.IsEmpty())
                {
                    DbgDetail = $" ({DbgDetail})";
                }

                if (!relevant.IsEmpty())
                {
                    relevant = $"Relevant='{relevant.Trim(", ".ToCharArray())}',";
                }

                if (!notrelevant.IsEmpty())
                {
                    notrelevant = $"Irrelevant='{notrelevant.Trim(", ".ToCharArray())}',";
                }

                if (!ignored.IsEmpty())
                {
                    ignored = $"Caused ignore='{ignored.Trim(", ".ToCharArray())}',";
                }

                if (!notenabled.IsEmpty())
                {
                    notenabled = $"Not Enabled={notenabled.Trim(" ,".ToCharArray())},";
                }

                if (!nottime.IsEmpty())
                {
                    nottime = $"Not Time={nottime.Trim(" ,".ToCharArray())},";
                }

                if (!nothreshold.IsEmpty())
                {
                    nothreshold = $"No Threshold Match='{nothreshold.Trim(" ,".ToCharArray())}',";
                }

                if (!percentsizewrong.IsEmpty())
                {
                    percentsizewrong = $"Percent Size wrong='{percentsizewrong.Trim(" ,".ToCharArray())}',";
                }

                if (!unknownobject.IsEmpty())
                {
                    unknownobject = $"Unknown Object='{unknownobject.Trim(" ,".ToCharArray())}',";
                }
                ;

                string maskignore = "";
                if (IgnoreImageMask)
                {
                    maskignore = " (Mask will be ignored)";
                }

                if (ret != ResultType.Relevant)
                {
                    AITOOL.Log($"Debug: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because: {notrelevant}{ignored}{notenabled}{nottime}{nothreshold}{percentsizewrong}{unknownobject}{relevant}. {maskignore}, {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}, IsNew={IsNew}");
                }
                else
                {
                    AITOOL.Log($"Debug: RelevantObjectManager: Object is valid for '{this.TypeName}{DbgDetail}' because:  {relevant}{notrelevant}{ignored}{notenabled}{nottime}{nothreshold}{percentsizewrong}{unknownobject}. {maskignore}, {preds.Count} predictions(s), Enabled={this.EnabledCount} of {this.ObjectList.Count}, IsNew={IsNew}");
                }
            }
            else
            {
                AITOOL.Log($"Debug: RelevantObjectManager: Skipping '{this.TypeName}{DbgDetail}' because there were no PREDICTIONS.");
            }

            return(ret);
        }