using System.IO.Ports;
using System.Management;
using System.Text.RegularExpressions;
namespace DB720Flasher;
///
/// WMI(Win32_PnPEntity)로 COM 포트를 전부 열거하고 VID/PID로 종류만 라벨링한다.
/// 어떤 포트도 제외하지 않는다 — JTAG로도, UART(CP210x/CH340/FTDI)로도 플래시할 수 있으므로
/// 전부 보여주고 사용자가 고른다. JTAG(303A:1001)는 맨 위로 올려 자동선택을 돕는다.
///
public static partial class PortScanner
{
[GeneratedRegex(@"\(COM(\d+)\)")] private static partial Regex ComRe();
[GeneratedRegex(@"VID_([0-9A-Fa-f]{4})")] private static partial Regex VidRe();
[GeneratedRegex(@"PID_([0-9A-Fa-f]{4})")] private static partial Regex PidRe();
public static List Scan()
{
var list = new List();
try
{
using var searcher = new ManagementObjectSearcher(
"SELECT Name, DeviceID FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'");
foreach (ManagementBaseObject mo in searcher.Get())
{
var name = mo["Name"] as string ?? "";
var devId = mo["DeviceID"] as string ?? "";
var cm = ComRe().Match(name);
if (!cm.Success) continue;
string? vid = VidRe().Match(devId) is { Success: true } v ? v.Groups[1].Value.ToUpperInvariant() : null;
string? pid = PidRe().Match(devId) is { Success: true } p ? p.Groups[1].Value.ToUpperInvariant() : null;
list.Add(new ComPortInfo
{
PortName = $"COM{cm.Groups[1].Value}",
Number = int.TryParse(cm.Groups[1].Value, out var n) ? n : 9999,
FriendlyName = name,
DeviceId = devId,
Vid = vid,
Pid = pid,
Kind = KindOf(vid, pid),
});
mo.Dispose();
}
}
catch { /* WMI 실패 시 빈 목록 */ }
// JTAG는 맨 위에, 그 중 '최신(번호 높은)' 것을 먼저 — 보드 리셋으로 번호가 올라가면
// 옛 번호는 유령 포트가 되므로 높은 번호가 살아있을 확률이 높다.
var jtag = list.Where(p => p.IsJtag).OrderByDescending(p => p.Number);
var rest = list.Where(p => !p.IsJtag).OrderBy(p => p.Number);
return jtag.Concat(rest).ToList();
}
///
/// 포트를 실제로 열 수 있는지 빠르게 확인(유령 포트 판별).
/// DTR/RTS를 건드리지 않아 보드를 리셋하지 않는다.
///
public static bool CanOpen(string portName)
{
try
{
using var sp = new SerialPort(portName) { DtrEnable = false, RtsEnable = false };
sp.Open();
return true;
}
catch { return false; }
}
static string KindOf(string? vid, string? pid) => (vid, pid) switch
{
("303A", "1001") => "USB-JTAG (내장)",
("10C4", _) => "CP210x",
("1A86", "7523") => "CH340",
("1A86", _) => "CH34x/CH9102",
("0403", _) => "FTDI",
("067B", _) => "PL2303",
_ => "USB 직렬",
};
}