chore: HERV 통합 저장소 초기 커밋
- 펌웨어(program), C# 대시보드(TestProgram), 시뮬레이터(Simulator), 프로토콜/문서(Protocol, doc) 전체를 단일 저장소로 통합 - program 폴더의 별도 git 저장소를 제거하고 통합 저장소에 흡수 - 빌드 산출물(program/build, bin/obj, *.o/.elf/.bin/.hex 등) .gitignore 처리 - 사내 Synology NAS Git 원격 연결 예정 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ERVSimulator.Protocol
|
||||
{
|
||||
// 공용 시리얼 채널 - byte 단위 수신 콜백 + 송신 helper
|
||||
public class SerialChannel : IDisposable
|
||||
{
|
||||
private SerialPort? _port;
|
||||
private CancellationTokenSource? _cts;
|
||||
private bool _disposed;
|
||||
|
||||
public string ChannelName { get; }
|
||||
public event Action<byte>? ByteReceived;
|
||||
public event Action<string>? Log;
|
||||
public event Action<bool>? ConnectionChanged;
|
||||
public bool IsConnected => _port?.IsOpen == true;
|
||||
|
||||
public SerialChannel(string channelName) { ChannelName = channelName; }
|
||||
|
||||
public static string[] GetAvailablePorts() => SerialPort.GetPortNames();
|
||||
|
||||
public bool Connect(string portName, int baudRate)
|
||||
{
|
||||
try
|
||||
{
|
||||
Disconnect();
|
||||
_port = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One)
|
||||
{
|
||||
ReadTimeout = 100,
|
||||
WriteTimeout = 200,
|
||||
Handshake = Handshake.None,
|
||||
DtrEnable = false,
|
||||
RtsEnable = false,
|
||||
};
|
||||
_port.Open();
|
||||
_cts = new CancellationTokenSource();
|
||||
_ = Task.Run(() => ReadLoop(_cts.Token));
|
||||
Log?.Invoke($"[{ChannelName}] Connected {portName} @ {baudRate}");
|
||||
ConnectionChanged?.Invoke(true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log?.Invoke($"[{ChannelName}] Connect FAIL: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
try { _cts?.Cancel(); } catch { }
|
||||
try { _port?.Close(); } catch { }
|
||||
_port?.Dispose();
|
||||
_port = null;
|
||||
ConnectionChanged?.Invoke(false);
|
||||
}
|
||||
|
||||
void ReadLoop(CancellationToken ct)
|
||||
{
|
||||
var buf = new byte[64];
|
||||
while (!ct.IsCancellationRequested && _port != null && _port.IsOpen)
|
||||
{
|
||||
try
|
||||
{
|
||||
int n = _port.Read(buf, 0, buf.Length);
|
||||
for (int i = 0; i < n; i++) ByteReceived?.Invoke(buf[i]);
|
||||
}
|
||||
catch (TimeoutException) { /* expected */ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log?.Invoke($"[{ChannelName}] ReadLoop error: {ex.Message}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Send(byte[] data, int length)
|
||||
{
|
||||
if (_port == null || !_port.IsOpen) return false;
|
||||
try
|
||||
{
|
||||
_port.Write(data, 0, length);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log?.Invoke($"[{ChannelName}] Send FAIL: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static class HexFormat
|
||||
{
|
||||
public static string Bytes(byte[] data, int length)
|
||||
{
|
||||
var sb = new System.Text.StringBuilder(length * 3);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (i > 0) sb.Append(' ');
|
||||
sb.Append(data[i].ToString("X2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user