feat(mcp): Dabit.Mcp 초기 공개 (리네임 + colors 수정 + scroll 검증)

- refactor: DabitChe.Mcp → Dabit.Mcp (namespace, csproj, exe 모두)
- fix: display_message colors를 쉼표 구분 문자열로 변경 (ModelContextProtocol 1.3.0 배열 바인딩 버그 우회)
- docs: scroll 4방향(left/right/up/down) 실기 검증 완료 (DIBD600T V3.6.1)

20 도구 (읽기 4 + 콘텐츠 2 + 설정 14), stdio 전송, Windows exe.
실기 검증: scroll/fill_screen 8색/display_message 글자별색 모두 정상.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
insulee
2026-06-02 11:20:49 +09:00
parent 94dd74bd5c
commit 640c9a3380
109 changed files with 7146 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
using System.ComponentModel;
using Dabit.Mcp.Services;
using DibdProtocol.Protocol;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
namespace Dabit.Mcp.Tools;
/// <summary>
/// 표출신호(HUB75 패턴) 설정 도구. ⚠️ 쓰기 — F16 영역(70+ 모델 의존).
/// 대표 신호만 이름으로 지원하고, 그 외는 send_custom_signal로 raw 코드 전송.
/// </summary>
[McpServerToolType]
public sealed class SignalTools
{
private readonly LedControlService _led;
private readonly ILogger<SignalTools> _log;
public SignalTools(LedControlService led, ILogger<SignalTools> log)
{
_led = led;
_log = log;
}
// 대표 표출신호 → HexData 4바이트 (정본 DisplaySignalData 일부). 전체 70+는 send_custom_signal로.
private static readonly Dictionary<string, byte[]> SignalHexData = new(StringComparer.OrdinalIgnoreCase)
{
["16D-P16D1S11"] = new byte[] { 0x00, 0x10, 0x01, 0x11 },
["16D-P16D1S10-1"] = new byte[] { 0x00, 0x10, 0x01, 0x01 },
["08D-P32D1S11"] = new byte[] { 0x00, 0x08, 0x01, 0x11 },
["08D-P64D1S21"] = new byte[] { 0x00, 0x08, 0x01, 0x21 },
["04D-P32D2S11"] = new byte[] { 0x00, 0x04, 0x02, 0x11 },
["04D-P64D1S11"] = new byte[] { 0x00, 0x04, 0x01, 0x11 },
["32D-P16D1S11"] = new byte[] { 0x00, 0x20, 0x01, 0x11 },
};
[McpServerTool]
[Description("전광판 표출신호(HUB75 패턴)를 이름으로 설정한다. ⚠️ 쓰기 — 잘못 설정하면 화면이 깨진다. " +
"지원: 16D-P16D1S11, 16D-P16D1S10-1, 08D-P32D1S11, 08D-P64D1S21, 04D-P32D2S11, 04D-P64D1S11, 32D-P16D1S11. " +
"목록에 없으면 send_custom_signal을 쓴다. (08D-P64D1S21은 138 IC 스캔 고정)")]
public async Task<string> SetDisplaySignal(
[Description("신호 이름. 예: \"16D-P16D1S11\"")] string signalName,
[Description("색 순서 0~6 (기본 0)")] int colorOrder = 0,
CancellationToken ct = default)
{
if (!SignalHexData.TryGetValue(signalName.Trim(), out var hd))
return $"지원하지 않는 신호 '{signalName}'. 지원 목록: {string.Join(", ", SignalHexData.Keys)}. 또는 send_custom_signal 사용.";
var colorHex = (byte)(Math.Clamp(colorOrder, 0, 6) + 1); // 정본: idx+1
byte[] payload = { hd[0], hd[1], hd[2], hd[3], colorHex, 0x01 }; // scanHex 기본 0x01(138 IC)
var resp = await _led.SendHexAsync(HexCommand.SetHub75Pattern, payload, ct, 5000);
var (ok, msg) = LedControlService.CheckWriteStatus(HexCommand.SetHub75Pattern, resp);
return ok ? $"표출신호 '{signalName}' 설정 (color={colorOrder})." : $"표출신호 설정 실패 — {msg}";
}
[McpServerTool]
[Description("사용자 지정 표출신호 코드를 직접 전송한다(고급). ⚠️ 쓰기 — 코드 형식을 정확히 알 때만 사용. 잘못된 값은 화면을 깨뜨릴 수 있다.")]
public async Task<string> SendCustomSignal(
[Description("신호 코드 문자열. 예: \"8121\"")] string code,
CancellationToken ct = default)
{
if (string.IsNullOrWhiteSpace(code) || code.Contains('/') || code.Contains("![") || code.Contains("!]"))
return "유효한 신호 코드가 아닙니다 ('/', '![', '!]' 불가).";
// 정본: ASCII msgType "44", data 맨 앞 공백 1개 필수
var resp = await _led.SendAsciiAsync("44", " " + code.Trim(), ct, 5000);
if (resp is null) return "전송 실패 — 연결 또는 통신 오류.";
return resp.Result == "F" ? "전광판이 거부했습니다 (Result=F)." : $"사용자 표출신호 '{code}' 전송.";
}
}