protected override async Task GetValueAsync(SortedDictionary <string, string> ret, RefInt nextPollingInterval, CancellationToken cancel = default)
        {
            var result = await EasyExec.ExecAsync(Consts.LinuxCommands.Df, "-a -B1");

            string[] lines = result.OutputStr._GetLines();

            string[] ignores = "/dev /run /sys /var /snap /tmp /proc"._Split(StringSplitOptions.RemoveEmptyEntries, " ");

            foreach (string line in lines)
            {
                string[] tokens = line._Split(StringSplitOptions.RemoveEmptyEntries, ' ', '\t');

                if (tokens.Length >= 6)
                {
                    string totalStr     = tokens[1];
                    string availableStr = tokens[3];
                    string path         = tokens[5];

                    if (totalStr != "-" && availableStr != "-")
                    {
                        if (path.StartsWith("/") && ignores.Where(x => path.StartsWith(x, StringComparison.OrdinalIgnoreCase)).Any() == false)
                        {
                            long total     = totalStr._ToLong();
                            long available = availableStr._ToLong();

                            if (total > 0 && available >= 0)
                            {
                                available = Math.Min(available, total);
                                ret.TryAdd($"available - {path}", NormalizeDoubleValue(((double)available * 100.0 / (double)total).ToString("F3")));
                            }
                        }
                    }
                }
            }
        }
 protected override void InitImpl()
 {
     // ConnTrack コマンドが利用可能かどうか確認
     if (EasyExec.ExecAsync(Consts.LinuxCommands.ConnTrack, "-C")._TryGetResult() != default)
     {
         IsConnTrackOk = true;
     }
 }
        protected override async Task GetValueAsync(SortedDictionary <string, string> ret, RefInt nextPollingInterval, CancellationToken cancel = default)
        {
            // sys/class/thermal/ から温度取得
            foreach (var thermalFile in this.ThermalFiles)
            {
                string value = (await Lfs.ReadStringFromFileAsync(thermalFile.Value))._GetFirstFilledLineFromLines();

                double d = ((double)value._ToInt()) / 1000.0;

                ret.TryAdd($"{thermalFile.Key}", NormalizeDoubleValue(d.ToString("F3")));
            }

            if (IsSensorsCommandOk)
            {
                try
                {
                    // Sensors コマンドで温度取得
                    var result = await EasyExec.ExecAsync(Consts.LinuxCommands.Sensors, "-u", cancel : cancel);

                    string[] lines = result.OutputStr._GetLines(true);

                    string groupName = "";
                    string fieldName = "";

                    foreach (string line2 in lines)
                    {
                        string line = line2.TrimEnd();

                        if (line.StartsWith(" ") == false && line._InStr(":") == false)
                        {
                            // グループ名
                            groupName = line.Trim();
                        }
                        else if (line.StartsWith(" ") == false && line.EndsWith(":"))
                        {
                            // 値名
                            fieldName = line.Substring(0, line.Length - 1);
                        }
                        else if (line.StartsWith(" ") && line._GetKeyAndValue(out string key, out string value, ":"))
                        {
                            // 値サブ名 : 値
                            key   = key.Trim();
                            value = value.Trim();

                            if (key.EndsWith("_input", StringComparison.OrdinalIgnoreCase))
                            {
                                ret.TryAdd($"{groupName}/{fieldName}", NormalizeDoubleValue(value));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    ex._Debug();
                }
            }
        }
        protected override async Task GetValueAsync(SortedDictionary <string, string> ret, RefInt nextPollingInterval, CancellationToken cancel = default)
        {
            var result = await EasyExec.ExecAsync(Consts.LinuxCommands.Free, "-b -w");

            string[] lines = result.OutputStr._GetLines();

            List <string> headers = new List <string>();

            KeyValueList <string, string> dataList = new KeyValueList <string, string>();

            foreach (string line in lines)
            {
                string[] tokens = line._Split(StringSplitOptions.RemoveEmptyEntries, ' ', '\t');

                if (tokens.Length >= 2)
                {
                    if (headers.Count == 0)
                    {
                        if (tokens[0]._IsSamei("total"))
                        {
                            // ヘッダ行
                            foreach (string token in tokens)
                            {
                                headers.Add(token);
                            }
                        }
                    }
                    else
                    {
                        // データ行
                        if (tokens[0]._IsSamei("Mem:"))
                        {
                            for (int i = 1; i < tokens.Length; i++)
                            {
                                if (headers.Count >= (i - 1))
                                {
                                    dataList.Add(headers[i - 1], tokens[i]);
                                }
                            }
                        }
                    }
                }
            }

            // total
            long total     = dataList._GetStrFirst("total", "-1")._ToLong();
            long available = dataList._GetStrFirst("available", "-1")._ToLong();

            if (total >= 0 && available >= 0)
            {
                available = Math.Min(available, total);
                ret.TryAdd($"available", NormalizeDoubleValue(((double)available * 100.0 / (double)total).ToString("F3")));
            }
        }
        protected override void InitImpl()
        {
            // sensors コマンドが利用可能かどうか確認
            if (EasyExec.ExecAsync(Consts.LinuxCommands.Sensors, "-u")._TryGetResult() != default)
            {
                IsSensorsCommandOk = true;
            }

            // /sys/class/thermal/ から取得可能な値一覧を列挙
            FileSystemEntity[]? dirList = null;
            try
            {
                dirList = Lfs.EnumDirectory(Consts.LinuxPaths.SysThermal);
            }
            catch { }

            if (dirList != null)
            {
                foreach (var dir in dirList)
                {
                    string fileName = Lfs.PathParser.Combine(dir.FullPath, "temp");

                    if (Lfs.IsFileExists(fileName))
                    {
                        try
                        {
                            Lfs.ReadStringFromFile(fileName);

                            ThermalFiles.Add(dir.Name, fileName);
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }
        async Task RunAndParseBirdAsync(string birdExeName, SortedDictionary <string, string> ret, int cmdIpVersion, CancellationToken cancel = default)
        {
            var result = await EasyExec.ExecAsync(birdExeName, "show protocol all", cancel : cancel);

            string body = result.OutputStr;

            string[] lines = body._GetLines(true);

            string first = lines.FirstOrDefault()._NonNullTrim();

            if (first.StartsWith("BIRD", StringComparison.OrdinalIgnoreCase) == false)
            {
                // BIRD 文字列が見つからない
                return;
            }

            if (first.StartsWith("BIRD 1.", StringComparison.OrdinalIgnoreCase))
            {
                // BIRD 1.x
            }
            else
            {
                // BIRD 2.x or later
                cmdIpVersion = 0;
            }

            string name     = "";
            string protocol = "";
            string table    = "";
            int    ipVer    = 0;

            foreach (string line in lines)
            {
                string[] tokens = line._Split(StringSplitOptions.RemoveEmptyEntries, " ", "\t");

                if (line.StartsWith(" ") == false)
                {
                    bool ok = false;
                    // あるプロトコルの開始
                    if (tokens.Length >= 5)
                    {
                        if (tokens[3]._IsSamei("up") || tokens[3]._IsSamei("start"))
                        {
                            name     = tokens[0];
                            protocol = tokens[1];
                            table    = tokens[2];

                            if (cmdIpVersion != 0)
                            {
                                ipVer = cmdIpVersion;
                            }

                            ok = true;
                        }
                    }

                    if (ok == false)
                    {
                        name  = protocol = table = "";
                        ipVer = 0;
                    }
                }
                else
                {
                    // プロトコルに関する情報
                    if (name._IsFilled())
                    {
                        if (tokens.Length >= 2)
                        {
                            if (cmdIpVersion == 0)
                            {
                                if (tokens[0]._IsSamei("Channel"))
                                {
                                    string verstr = tokens[1];

                                    if (verstr._IsSamei("ipv4"))
                                    {
                                        ipVer = 4;
                                    }
                                    else if (verstr._IsSamei("ipv6"))
                                    {
                                        ipVer = 6;
                                    }
                                    else
                                    {
                                        ipVer = 0;
                                    }
                                }
                            }
                        }

                        if (tokens.Length >= 5)
                        {
                            if (tokens[0]._IsSamei("Routes:"))
                            {
                                int imported  = tokens[1]._ToInt();
                                int exported  = tokens[3]._ToInt();
                                int preferred = 0;

                                if (tokens.Length >= 7)
                                {
                                    preferred = tokens[5]._ToInt();
                                }

                                if (ipVer != 0)
                                {
                                    string key = $"{protocol} - {name} - IPv{ipVer}";

                                    ret.TryAdd($"{key} - Import", imported.ToString());
                                    ret.TryAdd($"{key} - Export", exported.ToString());
                                    ret.TryAdd($"{key} - Prefer", preferred.ToString());
                                }
                            }
                        }
                    }
                }
            }
        }
        protected override async Task GetValueAsync(SortedDictionary <string, string> ret, RefInt nextPollingInterval, CancellationToken cancel = default)
        {
            if (IsConnTrackOk)
            {
                try
                {
                    // ConnTrack
                    var result = await EasyExec.ExecAsync(Consts.LinuxCommands.ConnTrack, "-C");

                    string valueStr = result.OutputStr._GetFirstFilledLineFromLines();

                    ret.TryAdd($"ConnTrack Sessions", valueStr._ToInt().ToString());
                }
                catch (Exception ex)
                {
                    ex._Debug();
                }
            }

            if (true)
            {
                try
                {
                    // Threads
                    var result = EasyExec.ExecBashAsync("ps -eo nlwp | tail -n +2 | awk '{ num_threads += $1 } END { print num_threads }'")._GetResult();

                    string valueStr = result.OutputStr._GetFirstFilledLineFromLines();

                    ret.TryAdd($"Threads", valueStr._ToInt().ToString());
                }
                catch (Exception ex)
                {
                    ex._Debug();
                }
            }

            if (true)
            {
                try
                {
                    // Sockets
                    string[] lines = (await Lfs.ReadStringFromFileAsync(Consts.LinuxPaths.SockStat, flags: FileFlags.NoCheckFileSize))._GetLines();

                    int numSockets = -1;
                    int numTcp     = -1;
                    int numUdp     = -1;

                    foreach (string line in lines)
                    {
                        string[] tokens = line._Split(StringSplitOptions.RemoveEmptyEntries, " ");

                        if (tokens.Length >= 3)
                        {
                            if (tokens[0]._IsSamei("sockets:"))
                            {
                                numSockets = tokens[2]._ToInt();
                            }

                            if (tokens[0]._IsSamei("TCP:"))
                            {
                                numTcp = tokens[2]._ToInt();
                            }

                            if (tokens[0]._IsSamei("UDP:"))
                            {
                                numUdp = tokens[2]._ToInt();
                            }
                        }
                    }

                    if (numSockets >= 0 && numTcp >= 0 && numUdp >= 0)
                    {
                        ret.TryAdd($"Sockets", numSockets.ToString());
                        ret.TryAdd($"TCP", numTcp.ToString());
                        ret.TryAdd($"UDP", numUdp.ToString());
                    }
                }
                catch (Exception ex)
                {
                    ex._Debug();
                }
            }
        }
        protected override async Task GetValueAsync(SortedDictionary <string, string> ret, RefInt nextPollingInterval, CancellationToken cancel = default)
        {
            SnmpWorkSettings settings = Host.Settings;

            if (settings.PingTargets._IsSamei("none") || settings.PingTargets._IsSamei("null"))
            {
                return;
            }

            string hopsStr = "Hops";

            if (settings.HopsToTTL)
            {
                hopsStr = "TTL";
            }

            string[] pingTargets = settings.PingTargets._Split(StringSplitOptions.RemoveEmptyEntries, ",");

            numPerform++;

            foreach (string pingTarget in pingTargets)
            {
                cancel.ThrowIfCancellationRequested();

                ParseTargetString(pingTarget, out string hostname, out string alias);

                bool ok = false;

                try
                {
                    IPAddress ipAddress = await LocalNet.GetIpAsync(hostname, cancel : cancel);

                    if (FirstPing.IsFirstCall())
                    {
                        // JIT 対策
                        try
                        {
                            await LocalNet.SendPingAsync(ipAddress, pingCancel : cancel);
                        }
                        catch { }
                    }

                    int numTry = 3;

                    if (numPerform >= 2)
                    {
                        // SpeedTest が動作中の場合は SpeedTest が完了するまで待機する
                        await TaskUtil.AwaitWithPollAsync(Timeout.Infinite, 10, () => !SpeedTestClient.IsInProgress, cancel);

                        // 試行回数を指定する
                        numTry = Math.Min(Math.Max(settings.PingNumTry, 1), 100);
                    }

                    SendPingReply reply = await LocalNet.SendPingAndGetBestResultAsync(ipAddress, pingCancel : cancel, numTry : numTry);

                    if (reply.Ok)
                    {
                        double rtt = reply.RttDouble;

                        rtt = Math.Min(rtt, 2.0);

                        int ttl = reply.Ttl;

                        bool ttl_ok = false;

                        if (ttl == 0)
                        {
                            // Use ping command to get TTL
                            try
                            {
                                var result = await EasyExec.ExecAsync(ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6?Consts.LinuxCommands.Ping6 : Consts.LinuxCommands.Ping,
                                                                      $"-W 1 -c 1 {ipAddress.ToString()}",
                                                                      cancel : cancel,
                                                                      throwOnErrorExitCode : false);

                                string[] lines = result.OutputStr._GetLines(true);

                                foreach (string line in lines)
                                {
                                    OneLineParams param  = new OneLineParams(line, ' ', false);
                                    string        ttlStr = param._GetStrFirst("ttl");
                                    if (ttlStr._IsFilled())
                                    {
                                        ttl    = ttlStr._ToInt();
                                        ttl_ok = true;
                                        break;
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                ex._Debug();
                            }
                        }
                        else
                        {
                            ttl_ok = true;
                        }

                        ret.TryAdd($"Time - {alias}", (rtt * 1000.0).ToString("F3"));


                        if (ttl > 128)
                        {
                            ttl -= 128;
                        }
                        else if (ttl > 64)
                        {
                            ttl -= 64;
                        }

                        int hops = 64 - ttl;

                        if (ttl_ok == false)
                        {
                            hops = 0;
                        }

                        hops._SetMax(0);
                        hops._SetMin(64);

                        ret.TryAdd($"{hopsStr} - {alias}", hops.ToString());

                        ok = true;
                    }
                }
                catch (Exception ex)
                {
                    ex._Debug();
                }

                if (ok == false)
                {
                    ret.TryAdd($"Time - {alias}", "");
                    ret.TryAdd($"{hopsStr} - {alias}", "0");
                }
            }
        }