235 lines
9.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO.Ports;
using UnityEngine;
using UnityEngine.UI;
namespace Aisu.CardReader.Samples
{
/// <summary>用于 Windows 实机联调的最小 Unity UI 示例。</summary>
public sealed class CardReaderSampleUI : MonoBehaviour
{
[SerializeField] private Dropdown portDropdown;
[SerializeField] private Button refreshButton;
[SerializeField] private Button openButton;
[SerializeField] private Button closeButton;
[SerializeField] private Text stateText;
[SerializeField] private Text portText;
[SerializeField] private Text cardTypeText;
[SerializeField] private Text cardNumberText;
[SerializeField] private Text lastEventTimeText;
[SerializeField] private Text errorText;
[SerializeField] private Text logText;
private readonly List<string> portNames = new List<string>();
private readonly List<string> logLines = new List<string>();
private CardReaderManager manager;
private bool operationInProgress;
private bool destroying;
private bool apiSubscribed;
private void Awake()
{
manager = CardReaderManager.Instance;
SetText(stateText, "状态:未连接");
SetText(portText, "端口:-");
SetText(cardTypeText, "CardType:-");
SetText(cardNumberText, "CardNumber:-");
SetText(lastEventTimeText, "最后事件:-");
SetText(errorText, "错误:-");
SetText(logText, "日志:-");
}
private void OnEnable()
{
refreshButton?.onClick.AddListener(RefreshPorts);
openButton?.onClick.AddListener(OpenSelectedPort);
closeButton?.onClick.AddListener(CloseReader);
SubscribeApiEvents();
}
private void Start()
{
// Manager 与 Sample 的 Awake 顺序不应影响事件订阅和首次端口扫描。
SubscribeApiEvents();
RefreshPorts();
}
private void SubscribeApiEvents()
{
if (apiSubscribed) return;
if (manager == null) manager = CardReaderManager.Instance;
if (manager == null) return;
CardReaderApi.OnConnected.AddListener(OnConnected);
CardReaderApi.OnDisconnected.AddListener(OnDisconnected);
CardReaderApi.OnStateChanged.AddListener(OnStateChanged);
CardReaderApi.OnCardReceived.AddListener(OnCardReceived);
CardReaderApi.OnError.AddListener(OnReaderError);
apiSubscribed = true;
}
private void OnDisable()
{
refreshButton?.onClick.RemoveListener(RefreshPorts);
openButton?.onClick.RemoveListener(OpenSelectedPort);
closeButton?.onClick.RemoveListener(CloseReader);
if (!apiSubscribed || manager == null) return;
CardReaderApi.OnConnected.RemoveListener(OnConnected);
CardReaderApi.OnDisconnected.RemoveListener(OnDisconnected);
CardReaderApi.OnStateChanged.RemoveListener(OnStateChanged);
CardReaderApi.OnCardReceived.RemoveListener(OnCardReceived);
CardReaderApi.OnError.RemoveListener(OnReaderError);
apiSubscribed = false;
}
public void RefreshPorts()
{
if (destroying || operationInProgress) return;
var previousPort = GetSelectedPort();
portNames.Clear();
portNames.AddRange(SerialPort.GetPortNames());
portNames.Sort(StringComparer.OrdinalIgnoreCase);
if (portDropdown != null)
{
var options = portNames.Count == 0
? new List<string> { "No Ports" }
: new List<string>(portNames);
portDropdown.ClearOptions();
portDropdown.AddOptions(options);
var selectedIndex = previousPort == null ? 0 : portNames.IndexOf(previousPort);
portDropdown.value = selectedIndex < 0 ? 0 : selectedIndex;
portDropdown.RefreshShownValue();
}
SetText(portText, portNames.Count == 0 ? "端口:未发现串口" : "端口:" + GetSelectedPort());
if (portNames.Count == 0) SetStateText("未发现串口");
AppendLog(portNames.Count == 0 ? "未发现可用串口。" : "发现串口:" + string.Join(", ", portNames));
UpdateButtons();
}
public void OpenSelectedPort()
{
if (destroying || operationInProgress || manager == null || portDropdown == null || portNames.Count == 0)
return;
operationInProgress = true;
UpdateButtons();
try
{
var selectedPort = GetSelectedPort();
if (string.IsNullOrEmpty(selectedPort)) return;
var current = manager.SerialOptions;
manager.SerialOptions = new SerialDeviceOptions
{
PortName = selectedPort, BaudRate = current.BaudRate,
Parity = current.Parity, DataBits = current.DataBits, StopBits = current.StopBits,
Handshake = current.Handshake, RtsEnable = current.RtsEnable, DtrEnable = current.DtrEnable,
ReadTimeout = current.ReadTimeout, WriteTimeout = current.WriteTimeout,
AutoReconnect = current.AutoReconnect, ReconnectInterval = current.ReconnectInterval,
MaxPendingSendCount = current.MaxPendingSendCount
};
CardReaderApi.Open();
SetText(portText, "端口:" + selectedPort);
AppendLog("已打开读卡器:" + selectedPort);
}
catch (Exception exception) { ShowError("连接失败", exception); }
finally { operationInProgress = false; UpdateButtons(); }
}
public void CloseReader()
{
if (destroying || operationInProgress || manager == null) return;
operationInProgress = true;
try
{
CardReaderApi.Close();
AppendLog("已关闭读卡器。");
}
catch (Exception exception) { ShowError("断开失败", exception); }
finally { operationInProgress = false; UpdateButtons(); }
}
private void OnConnected()
{
SetStateText("Connected");
AppendLog("读卡器已连接。");
UpdateButtons();
}
private void OnDisconnected()
{
SetStateText("Disconnected");
AppendLog("读卡器已断开。");
UpdateButtons();
}
private void OnStateChanged(SerialDeviceState state)
{
SetStateText(state.ToString());
AppendLog("状态:" + state);
UpdateButtons();
}
private void OnCardReceived(CardInfo card)
{
SetText(cardTypeText, $"CardType:{card.CardType} (0x{card.CardTypeCode:X2})");
SetText(cardNumberText, $"CardNumber:{card.CardNumber}");
SetText(lastEventTimeText, $"最后事件:{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
AppendLog("收到卡片:" + card.CardNumber);
}
private void OnReaderError(CardReaderErrorEventArgs args) { ShowError("Transport/Core 错误", args.Exception); }
private void OnDestroy()
{
destroying = true;
}
private void UpdateButtons()
{
var connected = manager != null && manager.IsConnected;
if (refreshButton != null) refreshButton.interactable = !operationInProgress && !connected;
if (openButton != null) openButton.interactable = !operationInProgress && !connected && portNames.Count > 0;
if (closeButton != null) closeButton.interactable = !operationInProgress && connected;
}
private void ShowError(string message, Exception exception)
{
var text = exception == null ? message : message + ":" + FormatException(exception);
SetText(errorText, "错误:" + text);
AppendLog(text);
Debug.LogError("[AISU 830T Sample] " + text, this);
}
private string GetSelectedPort()
{
if (portDropdown == null || portDropdown.value < 0 || portDropdown.value >= portNames.Count)
return null;
return portNames[portDropdown.value];
}
private void SetStateText(string state) { SetText(stateText, "状态:" + state); }
private void AppendLog(string message)
{
if (logText == null) return;
logLines.Add(DateTime.Now.ToString("HH:mm:ss.fff") + " " + message);
if (logLines.Count > 40) logLines.RemoveAt(0);
SetText(logText, "日志:\n" + string.Join("\n", logLines));
}
private static string FormatException(Exception exception)
{
var current = exception;
var result = exception.GetType().Name + ": " + exception.Message;
while (current.InnerException != null)
{
current = current.InnerException;
result += " | " + current.GetType().Name + ": " + current.Message;
}
return result;
}
private static void SetText(Text target, string value) { if (target != null) target.text = value; }
}
}