폴더·csproj·어셈블리명·RootNamespace·코드 namespace·x:Class·app.manifest 전부 변경. 빌드 결과가 네이티브로 FirmwareFlasher.exe 로 나온다(dist 수동 리네임 불필요). README 빌드/구조 경로도 갱신. 모델명 "DB720"은 그대로 유지. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
3.2 KiB
C#
83 lines
3.2 KiB
C#
using System.IO.Ports;
|
|
using System.Management;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace FirmwareFlasher;
|
|
|
|
/// <summary>
|
|
/// WMI(Win32_PnPEntity)로 COM 포트를 전부 열거하고 VID/PID로 종류만 라벨링한다.
|
|
/// 어떤 포트도 제외하지 않는다 — JTAG로도, UART(CP210x/CH340/FTDI)로도 플래시할 수 있으므로
|
|
/// 전부 보여주고 사용자가 고른다. JTAG(303A:1001)는 맨 위로 올려 자동선택을 돕는다.
|
|
/// </summary>
|
|
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<ComPortInfo> Scan()
|
|
{
|
|
var list = new List<ComPortInfo>();
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 포트를 실제로 열 수 있는지 빠르게 확인(유령 포트 판별).
|
|
/// DTR/RTS를 건드리지 않아 보드를 리셋하지 않는다.
|
|
/// </summary>
|
|
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 직렬",
|
|
};
|
|
}
|