using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using leak_test_project.Models;
using leak_test_project.Utils;
namespace leak_test_project.Infrastructure
{
///
/// 실제 ADLINK 하드웨어와 통신하는 DIO 보드 구현체.
/// Dask.cs 래퍼를 사용하여 PCIS-DASK API를 호출합니다.
///
public class RealDioBoard : IDioBoard
{
private short _cardNumber = -1;
private readonly Dictionary _inputs = new Dictionary();
private readonly Dictionary _outputs = new Dictionary();
private bool _disposed = false;
private CancellationTokenSource _pollingCts;
private Task _pollingTask;
private readonly object _ioLock = new object(); // 하드웨어 동시성 제어용 Lock
public event EventHandler InputChanged;
public event EventHandler ErrorOccurred;
public bool Initialize()
{
var config = DioConfigParser.LoadDefault();
if (config != null)
{
int bitIndex = 0;
foreach (var p in config.InputPoints)
{
p.BitIndex = bitIndex++;
_inputs[p.Name] = p;
}
bitIndex = 0;
foreach (var p in config.OutputPoints)
{
p.BitIndex = bitIndex++;
_outputs[p.Name] = p;
}
ushort cardType = GetCardTypeFromConfig(config.BoardType);
ushort boardNumber = (ushort)config.BoardNumber;
try
{
short ret = DASK.Register_Card(cardType, boardNumber);
if (ret < 0)
{
Console.WriteLine($"[RealDioBoard] Error registering ADLINK Card {config.BoardType}. Error code: {ret}");
return false;
}
_cardNumber = ret;
Console.WriteLine($"[RealDioBoard] Registered ADLINK Card {config.BoardType} (Handle: {_cardNumber})");
}
catch (Exception ex)
{
Console.WriteLine($"[RealDioBoard] Critical error during HW Registration: {ex.Message}");
// 드라이버가 없거나 하드웨어 접근 오류 시 앱이 죽지 않도록 false 반환
return false;
}
}
else
{
Console.WriteLine("[RealDioBoard] No INI config found.");
return false;
}
// Start background polling task for inputs
_pollingCts = new CancellationTokenSource();
_pollingTask = Task.Run(() => InputPollingLoop(_pollingCts.Token));
return true;
}
private ushort GetCardTypeFromConfig(string boardType)
{
switch (boardType.ToUpper())
{
case "PCI_7230": return DASK.PCI_7230;
case "PCI_7432": return DASK.PCI_7432;
case "PCI_7433": return DASK.PCI_7433;
case "PCI_7434": return DASK.PCI_7434;
case "PCI_7250": return DASK.PCI_7250;
// Add more if needed. Default to 7432
default:
Console.WriteLine($"[RealDioBoard] Unknown BoardType {boardType}. Defaulting to PCI_7432.");
return DASK.PCI_7432;
}
}
public bool ReadInput(string pointName)
{
if (_cardNumber < 0) return false;
if (_inputs.TryGetValue(pointName, out var point))
{
// Find port and line mathematically (Wait, actually we should use DIO port mapping)
// Assuming simple 1-to-1 mapping where index == line in Port 0.
int index = new List(_inputs.Values).IndexOf(point);
if (index >= 0)
{
ushort port = 0; // Usually port 0 for first 32 lines.
uint readValue;
short ret = DASK.DI_ReadPort((ushort)_cardNumber, port, out readValue);
if (ret >= 0)
{
bool isOn = (readValue & (1U << index)) != 0;
point.Value = isOn;
return isOn;
}
}
}
return false;
}
public void WriteOutput(string pointName, bool value)
{
if (_outputs.TryGetValue(pointName, out var point))
{
// 화면의 상태(불빛)는 하드웨어 연동 여부와 관계없이 항상 업데이트합니다. (원자적 연산)
point.Value = value;
// 실제 하드웨어가 없으면 물리적 제어 부분만 생략합니다.
if (_cardNumber < 0) return;
int index = point.BitIndex;
ushort port = 0;
// === 스레드 안전성 확보 (경쟁 조건 방지) ===
lock (_ioLock)
{
uint outValue;
DASK.DO_ReadPort((ushort)_cardNumber, port, out outValue);
if (value)
outValue |= (1U << index);
else
outValue &= ~(1U << index);
DASK.DO_WritePort((ushort)_cardNumber, port, outValue);
}
// Console.WriteLine($"[RealDioBoard] Set {pointName} (Line {index}) to {(value ? "ON" : "OFF")}");
}
}
private async Task InputPollingLoop(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
try
{
if (_cardNumber >= 0)
{
ushort port = 0;
uint readValue;
short ret;
lock (_ioLock)
{
ret = DASK.DI_ReadPort((ushort)_cardNumber, port, out readValue);
}
if (ret >= 0)
{
foreach (var point in _inputs.Values)
{
bool isCurrentlyOn = (readValue & (1U << point.BitIndex)) != 0;
// If changed from OFF to ON
if (isCurrentlyOn && !point.Value)
{
point.Value = true;
// Console.WriteLine($"[RealDioBoard] Input {point.Name} triggered (OFF→ON)");
InputChanged?.Invoke(this, new DioEventArgs(point.Name, true));
}
// If changed from ON to OFF
else if (!isCurrentlyOn && point.Value)
{
point.Value = false;
}
}
}
else
{
FileLogger.Log("ERROR", $"[RealDioBoard] DI_ReadPort Error: {ret}");
}
}
}
catch (Exception ex)
{
FileLogger.Log("ERROR", $"[RealDioBoard] Inner Polling Error: {ex.Message}");
}
// 10ms polling rate (approx)
await Task.Delay(10, token);
}
}
catch (TaskCanceledException)
{
// Expected when disposing
}
catch (Exception ex)
{
FileLogger.Log("ERROR", $"[RealDioBoard] Critical Polling Loop Error: {ex.Message}");
}
}
public List GetInputPoints()
{
return new List(_inputs.Values);
}
public List GetOutputPoints()
{
return new List(_outputs.Values);
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
if (_pollingCts != null)
{
_pollingCts.Cancel();
_pollingCts.Dispose();
}
if (_cardNumber >= 0)
{
DASK.Release_Card((ushort)_cardNumber);
Console.WriteLine("[RealDioBoard] Released ADLINK Card.");
_cardNumber = -1;
}
}
}
}
}