162 lines
5.0 KiB
C#
Raw Normal View History

2026-06-10 11:16:46 +08:00
using System.IO.Ports;
using System.Net.Http;
using System.Text;
using System.Text.Json;
2026-06-02 10:30:05 +08:00
using Microsoft.Extensions.Options;
using YD_TrainingCompetition_Api.Options;
namespace YD_TrainingCompetition_Api.Serial;
public sealed class SerialGateway : ISerialGateway
{
2026-06-10 11:16:46 +08:00
private static readonly HttpClient DebugClient = new();
2026-06-02 10:30:05 +08:00
private readonly SerialPort _serialPort;
public SerialGateway(IOptions<Serial24GOptions> options)
{
var cfg = options.Value;
_serialPort = new SerialPort
{
PortName = cfg.PortName,
BaudRate = cfg.BaudRate,
DataBits = cfg.DataBits,
StopBits = StopBits.One,
Parity = Parity.None,
ReadBufferSize = cfg.ReadBufferSize,
WriteBufferSize = cfg.WriteBufferSize
};
_serialPort.DataReceived += (_, _) =>
{
try
{
var len = _serialPort.BytesToRead;
if (len <= 0)
{
return;
}
var buf = new byte[len];
_serialPort.Read(buf, 0, len);
2026-06-10 11:16:46 +08:00
#region debug-point A:serial-receive
_ = DebugReportAsync("A", "SerialGateway.DataReceived", "[DEBUG] serial bytes received", new
{
port = _serialPort.PortName,
length = len,
hex = Convert.ToHexString(buf)
});
#endregion
2026-06-02 10:30:05 +08:00
DataReceived?.Invoke(this, buf);
}
catch
{
}
};
}
public event EventHandler<byte[]>? DataReceived;
public bool IsOpen => _serialPort.IsOpen;
public string? CurrentPort => _serialPort.IsOpen ? _serialPort.PortName : null;
public string[] GetAvailablePorts() => SerialPort.GetPortNames().OrderBy(x => x, StringComparer.OrdinalIgnoreCase).ToArray();
public Task OpenAsync(string portName, CancellationToken cancellationToken = default)
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
}
_serialPort.PortName = portName;
_serialPort.Open();
2026-06-10 11:16:46 +08:00
#region debug-point A:serial-open
_ = DebugReportAsync("A", "SerialGateway.OpenAsync", "[DEBUG] serial opened", new
{
port = _serialPort.PortName,
baudRate = _serialPort.BaudRate,
dataBits = _serialPort.DataBits,
readBufferSize = _serialPort.ReadBufferSize,
writeBufferSize = _serialPort.WriteBufferSize
});
#endregion
2026-06-02 10:30:05 +08:00
return Task.CompletedTask;
}
public Task CloseAsync(CancellationToken cancellationToken = default)
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
}
return Task.CompletedTask;
}
2026-06-10 11:16:46 +08:00
public void ClearInputBuffer()
{
if (_serialPort.IsOpen)
{
_serialPort.DiscardInBuffer();
}
}
2026-06-02 10:30:05 +08:00
public Task SendAsync(byte[] data, CancellationToken cancellationToken = default)
{
if (!_serialPort.IsOpen)
{
throw new InvalidOperationException("Serial port is not open");
}
2026-06-10 11:16:46 +08:00
#region debug-point B:serial-send
_ = DebugReportAsync("B", "SerialGateway.SendAsync", "[DEBUG] serial bytes sent", new
{
port = _serialPort.PortName,
length = data.Length,
hex = Convert.ToHexString(data)
});
#endregion
2026-06-02 10:30:05 +08:00
_serialPort.Write(data, 0, data.Length);
return Task.CompletedTask;
}
2026-06-10 11:16:46 +08:00
private static async Task DebugReportAsync(string hypothesisId, string location, string msg, object data)
{
try
{
var url = "http://127.0.0.1:7777/event";
var sessionId = "serial-gateway-download";
var envPath = Path.Combine(Directory.GetCurrentDirectory(), ".dbg", "serial-gateway-download.env");
if (File.Exists(envPath))
{
foreach (var line in await File.ReadAllLinesAsync(envPath))
{
if (line.StartsWith("DEBUG_SERVER_URL=", StringComparison.Ordinal))
{
url = line["DEBUG_SERVER_URL=".Length..].Trim();
}
else if (line.StartsWith("DEBUG_SESSION_ID=", StringComparison.Ordinal))
{
sessionId = line["DEBUG_SESSION_ID=".Length..].Trim();
}
}
}
var payload = JsonSerializer.Serialize(new
{
sessionId,
runId = "pre-fix",
hypothesisId,
location,
msg,
data,
ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
});
using var content = new StringContent(payload, Encoding.UTF8, "application/json");
await DebugClient.PostAsync(url, content);
}
catch
{
}
}
2026-06-02 10:30:05 +08:00
}