public FanController(IComputer computer,
                             string controlledSensorId,
                             List <ControlSignal> signals,
                             int minTarget,
                             int maxTarget,
                             int tickInterval,
                             SlowTargetController slowTargetController,
                             bool isLogEnabled)
        {
            TickInterval  = System.TimeSpan.FromSeconds(tickInterval);
            this.computer = computer;
            this.computer.HardwareAdded   += HardwareAdded;
            this.computer.HardwareRemoved += HardwareRemoved;

            this.controlledSensorId = controlledSensorId;

            this.slowTargetController = slowTargetController;

            this.signals = signals;

            this.minTarget = minTarget;
            this.maxTarget = maxTarget;

            lastEffectiveTarget = minTarget - 1;

            this.initialUpdateIsDone = false;
            this.isLogEnabled        = isLogEnabled;
        }
        public bool LoadConfig(string configFileName)
        {
            XmlDocument doc = new XmlDocument();

            try
            {
                doc.Load(configFileName);
            }
            catch
            {
                // File doesn't exist?
                return(true);
            }

            bool alwaysSaveLog = false;

            ParseLog parseLog = new ParseLog();

            XmlNodeList list = doc.GetElementsByTagName("fanControllers");

            foreach (XmlNode node in list)
            {
                XmlNode parent = node.ParentNode;
                if (parent != null && parent.Name == "configuration" && parent.ParentNode is XmlDocument)
                {
                    parseLog.Begin("fanControllers");

                    alwaysSaveLog = ReadOptionalString(node.Attributes, "alwaysSaveParsingLog", "no", parseLog) == "yes";

                    this.isLogEnabled = ReadOptionalString(node.Attributes, "enableLog", "no", parseLog) == "yes";

                    if (this.isLogEnabled)
                    {
                        log.TraceInformation("----------------- started -----------------");
                        log.TraceInformation("LoadConfig: loading {0}", configFileName);
                    }

                    foreach (XmlNode fcNode in node.ChildNodes)
                    {
                        if (fcNode.Name == "fanController")
                        {
                            parseLog.Begin("fanController");

                            XmlAttributeCollection fcAttrs = fcNode.Attributes;

                            if (!ReadString(fcAttrs, "controlledSensorId", out string controlledSensorId, parseLog) ||
                                !ReadInt(fcAttrs, "minTarget", out int minTarget, parseLog) ||
                                !ReadInt(fcAttrs, "maxTarget", out int maxTarget, parseLog) ||
                                !ReadInt(fcAttrs, "tickIntervalSeconds", out int tickIntervalSeconds, parseLog))
                            {
                                continue;
                            }

                            bool fcLogEnabled = ReadOptionalString(fcAttrs, "enableLog", "no", parseLog) == "yes";

                            SlowTargetController slowTargetController = null;

                            List <ControlSignal> signals = new List <ControlSignal>();

                            foreach (XmlNode signalNode in fcNode.ChildNodes)
                            {
                                if (signalNode.Name == "controlSignal")
                                {
                                    parseLog.Begin("controlSignal");

                                    XmlAttributeCollection signalAttrs = signalNode.Attributes;

                                    if (!ReadString(signalAttrs, "id", out string signalId, parseLog) ||
                                        !ReadInt(signalAttrs, "smaPeriod", out int smaPeriod, parseLog))
                                    {
                                        continue;
                                    }

                                    bool signalLogEnabled = ReadOptionalString(signalAttrs, "enableLog", "no", parseLog) == "yes";

                                    List <ControlSignal.Point> points = new List <ControlSignal.Point>();

                                    foreach (XmlNode pointNode in signalNode.ChildNodes)
                                    {
                                        if (pointNode.Name == "point")
                                        {
                                            parseLog.Begin("point");

                                            XmlAttributeCollection pointAttrs = pointNode.Attributes;

                                            if (ReadFloat(pointAttrs, "value", out float pvalue, parseLog) &&
                                                ReadFloat(pointAttrs, "target", out float ptarget, parseLog))
                                            {
                                                points.Add(new ControlSignal.Point {
                                                    value = pvalue, target = ptarget
                                                });
                                            }
                                            else
                                            {
                                                continue;
                                            }

                                            parseLog.End("point");
                                        }
                                    }

                                    if (points.Count == 0)
                                    {
                                        parseLog.AddError(@"Points are not defined (<point> elements)");
                                        continue;
                                    }
                                    else if (points.Count == 1)
                                    {
                                        parseLog.AddWarning(@"Only single point defined. Signal target will be fixed.");
                                        continue;
                                    }

                                    signals.Add(new ControlSignal(signalId, points, smaPeriod, signalLogEnabled));

                                    parseLog.End("controlSignal");
                                }
                                else if (signalNode.Name == "slowTargetController")
                                {
                                    parseLog.Begin("slowTargetController");

                                    if (slowTargetController != null)
                                    {
                                        parseLog.AddError("slowTargetController already defined for current fanController.");
                                        continue;
                                    }

                                    XmlAttributeCollection stcAttrs = signalNode.Attributes;

                                    if (ReadInt(stcAttrs, "smaPeriod", out int smaPeriod, parseLog) &&
                                        ReadInt(stcAttrs, "delayPeriod", out int delayPeriod, parseLog))
                                    {
                                        bool logEnabled = ReadOptionalString(stcAttrs, "enableLog", "no", parseLog) == "yes";
                                        slowTargetController = new SlowTargetController(smaPeriod, delayPeriod, minTarget, logEnabled);
                                    }

                                    parseLog.End("slowTargetController");
                                }
                            }

                            if (signals.Count == 0)
                            {
                                parseLog.AddError(@"Control signals are not defined (<controlSignal> elements).");
                                continue;
                            }
                            fanControllers.Add(new FanController(computer, controlledSensorId, signals,
                                                                 minTarget, maxTarget, tickIntervalSeconds, slowTargetController, fcLogEnabled));

                            parseLog.End("fanController");
                        }
                    }
                    parseLog.End("fanControllers");
                }