235 lines
9.4 KiB
C#
235 lines
9.4 KiB
C#
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; }
|
||
}
|
||
}
|