using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Aisu.SIM1.Face.Core;
using Aisu.SIM1.Face.Models;
using Aisu.SIM1.Face.Protocol;
using Aisu.SIM1.Face.UI.Common;
using UnityEngine;
using UnityEngine.UI;
namespace Aisu.SIM1.Face.UI.Pages
{
public sealed class FaceConnectPage : FaceModulePageBase
{
// Inspector 绑定字段。
[SerializeField] private InputField m_portInput;
// Inspector 绑定字段。
[SerializeField] private InputField m_baudRateInput;
// Inspector 绑定字段。
[SerializeField] private Toggle m_mockToggle;
// Inspector 绑定字段。
[SerializeField] private Text m_connectionText;
// Inspector 绑定字段。
[SerializeField] private Text m_readyText;
// Inspector 绑定字段。
[SerializeField] private Text m_moduleStatusText;
// Inspector 绑定字段。
[SerializeField] private Text m_licenseText;
// Inspector 绑定字段。
[SerializeField] private Text m_versionText;
// Inspector 绑定字段。
[SerializeField] private Text m_snText;
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "连接 / 基础状态", 30);
var form = builder.Section(transform, "通信配置");
m_portInput = builder.Input(form.transform, "串口号", "COM3");
m_baudRateInput = builder.Input(form.transform, "波特率", "115200");
m_mockToggle = builder.Toggle(form.transform, "Editor 使用 Mock 模式", IsEditorMockEnabled());
var row = builder.Row(form.transform);
builder.Button(row.transform, "打开连接", OpenConnection);
builder.Button(row.transform, "关闭连接", CloseConnection);
builder.Button(row.transform, "等待 READY", WaitReady);
var actions = builder.Section(transform, "基础命令");
var actionRow = builder.Row(actions.transform);
builder.Button(actionRow.transform, "RESET", ResetModule);
builder.Button(actionRow.transform, "GET STATUS", GetStatus);
builder.Button(actionRow.transform, "GET VERSION", GetVersion);
builder.Button(actionRow.transform, "GET SN", GetSn);
var info = builder.Section(transform, "状态");
m_connectionText = builder.Text(info.transform, "连接状态: 未连接");
m_readyText = builder.Text(info.transform, "READY: 未 READY");
m_moduleStatusText = builder.Text(info.transform, "模组状态: Invalid");
m_licenseText = builder.Text(info.transform, "授权: Mock 模式不需要授权");
m_versionText = builder.Text(info.transform, "版本: -");
m_snText = builder.Text(info.transform, "SN: -");
m_resultText = builder.Text(info.transform, "结果: -");
}
/// 打开通信连接。
///
/// 执行 OpenConnection 方法。
///
public void OpenConnection()
{
RunAsync(async () =>
{
var useMock = m_mockToggle != null && m_mockToggle.isOn;
var baud = ReadInt(m_baudRateInput, 115200);
if (!useMock && !ValidateLicenseBeforeOpen())
{
return;
}
m_manager.Configure(useMock, ReadText(m_portInput), baud);
m_manager.Open();
if (useMock)
{
await m_manager.WaitReadyAsync();
RefreshState();
SetResult("Mock 模组已连接");
return;
}
try
{
await m_manager.WaitReadyAsync();
RefreshState();
SetResult("硬件连接成功,已收到 READY");
}
catch
{
try
{
var status = await m_manager.ProbeReadyByStatusAsync();
RefreshState();
m_moduleStatusText.text = "模组状态: " + status;
SetResult("硬件连接成功,未收到 READY NOTE,但 GET STATUS 探测成功");
}
catch
{
m_manager.Close();
RefreshState();
throw new TimeoutException("未收到硬件 READY,GET STATUS 也无响应,请检查串口、波特率、电源和接线");
}
}
});
}
/// 关闭通信连接。
///
/// 执行 CloseConnection 方法。
///
public void CloseConnection()
{
m_manager.Close();
RefreshState();
SetResult("连接已关闭");
}
/// 等待 READY NOTE。
///
/// 执行 WaitReady 方法。
///
public void WaitReady()
{
RunAsync(async () =>
{
await m_manager.WaitReadyAsync();
RefreshState();
SetResult("模组 READY");
});
}
/// 复位模组。
///
/// 执行 ResetModule 方法。
///
public void ResetModule()
{
RunAsync(async () => { if (RequireConnected()) { await m_manager.Basic.ResetAsync(); SetResult("RESET 成功"); } });
}
/// 获取模组状态。
///
/// 执行 GetStatus 方法。
///
public void GetStatus()
{
RunAsync(async () =>
{
if (!RequireConnected()) return;
var status = await m_manager.ProbeReadyByStatusAsync();
RefreshState();
m_moduleStatusText.text = "模组状态: " + status;
SetResult("状态获取成功");
});
}
/// 获取版本。
///
/// 执行 GetVersion 方法。
///
public void GetVersion()
{
RunAsync(async () =>
{
if (!RequireConnected()) return;
m_versionText.text = "版本: " + await m_manager.Basic.GetVersionAsync();
SetResult("版本获取成功");
});
}
/// 获取 SN。
///
/// 执行 GetSn 方法。
///
public void GetSn()
{
RunAsync(async () =>
{
if (!RequireConnected()) return;
m_snText.text = "SN: " + await m_manager.Basic.GetSnAsync();
SetResult("SN 获取成功");
});
}
///
/// 执行 RefreshState 方法。
///
private void RefreshState()
{
var state = m_manager.IsReady ? "硬件已连接" : (m_manager.IsOpen ? "端口已打开,未 READY" : "未连接");
m_connectionText.text = "连接状态: " + state;
m_readyText.text = "READY: " + (m_manager.IsReady ? "READY" : "未 READY");
RefreshLicenseState(m_mockToggle != null && m_mockToggle.isOn);
}
///
/// 真实串口连接前检查授权。
///
private bool ValidateLicenseBeforeOpen()
{
var path = FaceLicenseManager.GetDefaultLicensePath();
var status = FaceLicenseManager.LoadAndVerify(path);
if (status == FaceLicenseStatus.Valid)
{
RefreshLicenseState(false);
return true;
}
var message = "授权无效,无法打开真实串口。路径: " + path + ",原因: " + FaceLicenseManager.LastError;
if (m_licenseText != null)
{
m_licenseText.text = "授权: " + message;
}
SetResult(message);
return false;
}
///
/// 刷新授权状态显示。
///
private void RefreshLicenseState(bool useMock)
{
if (m_licenseText == null)
{
return;
}
if (useMock)
{
m_licenseText.text = "授权: Mock 模式不需要授权";
return;
}
if (FaceLicenseManager.Status == FaceLicenseStatus.Valid && FaceLicenseManager.Payload != null)
{
m_licenseText.text = "授权: 有效,剩余 " + FaceLicenseVerifier.GetRemainingDays(FaceLicenseManager.Payload) + " 天";
return;
}
m_licenseText.text = "授权: 未验证,文件路径 " + FaceLicenseManager.GetDefaultLicensePath();
}
private bool IsEditorMockEnabled()
{
if (m_manager == null)
{
return true;
}
return m_manager.Settings != null ? m_manager.Settings.UseMockInEditor : m_manager.Config.UseMockInEditor;
}
}
public sealed class FaceProtocolDebugPage : FaceModulePageBase
{
// Inspector 绑定字段。
[SerializeField] private InputField m_hexInput;
// Inspector 绑定字段。
[SerializeField] private Text m_parseText;
private readonly FacePacketParser m_parser = new FacePacketParser();
private FaceModuleManager m_boundManager; // 已订阅事件的 Manager。
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "协议调试", 30);
m_hexInput = builder.Input(transform, "EF AA 10 00 00 10", "EF AA 10 00 00 10");
var row = builder.Row(transform);
builder.Button(row.transform, "发送 Hex", SendHex);
builder.Button(row.transform, "清空输入", () => m_hexInput.text = string.Empty);
m_parseText = builder.Text(transform, "解析结果: -", 20, TextAnchor.UpperLeft);
m_resultText = builder.Text(transform, "结果: -");
}
public override void Init(FaceModuleManager manager, FaceModuleLogPanel logPanel, FaceModuleLoadingView loadingView)
{
UnbindManagerEvents();
base.Init(manager, logPanel, loadingView);
m_boundManager = m_manager;
m_manager.OnRawLog += ParseRaw;
}
private void OnDestroy()
{
UnbindManagerEvents();
}
/// 发送原始 Hex 数据。
///
/// 执行 SendHex 方法。
///
public void SendHex()
{
try
{
if (!RequireConnected()) return;
var bytes = ParseHex(ReadText(m_hexInput));
m_manager.SendRawBytes(bytes);
SetResult("Hex 已发送");
}
catch (Exception ex) { SetError(ex); }
}
///
/// 执行 ParseRaw 方法。
///
private void ParseRaw(string line)
{
var index = line.IndexOf(' ');
if (index < 0) return;
try
{
var packets = m_parser.Push(ParseHex(line.Substring(index + 1)));
foreach (var packet in packets)
{
m_parseText.text = $"MsgID=0x{packet.MsgId:X2}\nSize={packet.Size}\nData={BitConverter.ToString(packet.Data)}\nChecksum=0x{packet.Checksum:X2}\nValid={packet.IsValid}";
}
}
catch { }
}
///
/// 执行 ParseHex 方法。
///
private static byte[] ParseHex(string text)
{
var parts = text.Split(new[] { ' ', '\t', '\r', '\n', '-' }, StringSplitOptions.RemoveEmptyEntries);
var bytes = new byte[parts.Length];
for (var i = 0; i < parts.Length; i++)
{
bytes[i] = Convert.ToByte(parts[i], 16);
}
return bytes;
}
private void UnbindManagerEvents()
{
if (m_boundManager != null)
{
m_boundManager.OnRawLog -= ParseRaw;
m_boundManager = null;
}
}
}
public sealed class FaceVerifyPage : FaceModulePageBase
{
// 内部状态字段。
private InputField m_timeoutInput;
// 内部状态字段。
private InputField m_maxTimesInput;
// 内部状态字段。
private Toggle m_autoToggle;
// 内部状态字段。
private Text m_faceStateText;
// 内部状态字段。
private Text m_poseText;
// 内部状态字段。
private Text m_boxText;
// 内部状态字段。
private Text m_userIdText;
// 内部状态字段。
private Text m_userNameText;
// 内部状态字段。
private Text m_adminText;
// 内部状态字段。
private Text m_unlockText;
private FaceModuleManager m_boundManager; // 已订阅事件的 Manager。
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "人脸验证", 30);
m_timeoutInput = builder.Input(transform, "超时时间", "10");
m_maxTimesInput = builder.Input(transform, "最大识别次数", "30");
m_autoToggle = builder.Toggle(transform, "自动探测验证", false);
var row = builder.Row(transform);
builder.Button(row.transform, "开始验证", StartVerify);
builder.Button(row.transform, "停止 / RESET", StopVerify);
m_faceStateText = builder.Text(transform, "人脸状态: -");
m_poseText = builder.Text(transform, "Yaw/Pitch/Roll: -");
m_boxText = builder.Text(transform, "人脸框: -");
m_userIdText = builder.Text(transform, "UserID: -");
m_userNameText = builder.Text(transform, "UserName: -");
m_adminText = builder.Text(transform, "Admin: -");
m_unlockText = builder.Text(transform, "UnlockStatus: -");
m_resultText = builder.Text(transform, "结果: -");
}
public override void Init(FaceModuleManager manager, FaceModuleLogPanel logPanel, FaceModuleLoadingView loadingView)
{
UnbindManagerEvents();
base.Init(manager, logPanel, loadingView);
m_boundManager = m_manager;
m_manager.OnFaceStateUpdated += OnFaceState;
m_manager.OnAutoVerifyResult += OnAutoVerifyResult;
}
private void OnDestroy()
{
UnbindManagerEvents();
}
/// 开始验证或自动验证。
///
/// 执行 StartVerify 方法。
///
public void StartVerify()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
if (m_autoToggle.isOn)
{
await m_manager.StartAutoVerifyAsync(ReadByte(m_timeoutInput, 10));
SetResult("自动验证已开启");
return;
}
var result = await m_manager.Verify.VerifyAsync(ReadByte(m_timeoutInput, 10), ReadByte(m_maxTimesInput, 30));
ShowResult(result);
});
}
/// 停止验证。
///
/// 执行 StopVerify 方法。
///
public void StopVerify()
{
RunAsync(async () =>
{
if (!RequireConnected()) return;
if (m_manager.IsAutoVerifyEnabled)
{
await m_manager.StopAutoVerifyAsync();
SetResult("自动验证已关闭");
return;
}
await m_manager.Basic.ResetAsync();
SetResult("已停止验证");
});
}
///
/// 执行 OnFaceState 方法。
///
private void OnFaceState(FaceFaceStateNote state)
{
m_faceStateText.text = "人脸状态: " + FaceProtocolEncoding.FaceStateMessage(state.State);
m_poseText.text = $"Yaw/Pitch/Roll: {state.Yaw}/{state.Pitch}/{state.Roll}";
m_boxText.text = $"人脸框: L{state.Left} T{state.Top} R{state.Right} B{state.Bottom}";
}
///
/// 执行 ShowResult 方法。
///
private void ShowResult(FaceVerifyResult result)
{
m_userIdText.text = "UserID: " + result.UserId;
m_userNameText.text = "UserName: " + result.UserName;
m_adminText.text = "Admin: " + result.IsAdmin;
m_unlockText.text = "UnlockStatus: " + result.UnlockStatus;
SetResult(result.Success ? "验证成功" : FaceProtocolEncoding.ResultMessage(result.ResultCode));
}
private void OnAutoVerifyResult(FaceAutoVerifyResult result)
{
ShowResult(result.VerifyResult);
}
private void UnbindManagerEvents()
{
if (m_boundManager != null)
{
m_boundManager.OnFaceStateUpdated -= OnFaceState;
m_boundManager.OnAutoVerifyResult -= OnAutoVerifyResult;
m_boundManager = null;
}
}
}
public sealed class FaceEnrollPage : FaceModulePageBase
{
// 内部状态字段。
private InputField m_nameInput;
// 内部状态字段。
private Toggle m_adminToggle;
// 内部状态字段。
private Dropdown m_modeDropdown;
// 内部状态字段。
private Dropdown m_directionDropdown;
// 内部状态字段。
private Dropdown m_enrollTypeDropdown;
// 内部状态字段。
private Toggle m_duplicateToggle;
// 内部状态字段。
private InputField m_timeoutInput;
// 内部状态字段。
private Text m_faceStateText;
// 内部状态字段。
private Text m_poseText;
// 内部状态字段。
private Text m_directionText;
// 内部状态字段。
private Text m_userIdText;
private FaceModuleManager m_boundManager; // 已订阅事件的 Manager。
private Action m_faceStateHandler; // 人脸状态事件回调。
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "人脸录入", 30);
m_nameInput = builder.Input(transform, "用户姓名", "User");
m_adminToggle = builder.Toggle(transform, "管理员", false);
m_modeDropdown = builder.Dropdown(transform, new[] { "交互录入 ENROLL", "单帧录入 ENROLL_SINGLE", "集成录入 ENROLL_ITG" });
m_directionDropdown = builder.Dropdown(transform, new[] { "正脸", "朝上", "朝下", "朝左", "朝右" });
m_enrollTypeDropdown = builder.Dropdown(transform, new[] { "交互", "单帧" });
m_duplicateToggle = builder.Toggle(transform, "允许重复录入", false);
m_timeoutInput = builder.Input(transform, "超时时间", "20");
var row = builder.Row(transform);
builder.Button(row.transform, "开始录入", StartEnroll);
builder.Button(row.transform, "取消录入", CancelEnroll);
m_faceStateText = builder.Text(transform, "人脸状态: -");
m_poseText = builder.Text(transform, "姿态: -");
m_directionText = builder.Text(transform, "已录入方向: -");
m_userIdText = builder.Text(transform, "最终 UserID: -");
m_resultText = builder.Text(transform, "ResultCode: -");
}
public override void Init(FaceModuleManager manager, FaceModuleLogPanel logPanel, FaceModuleLoadingView loadingView)
{
UnbindManagerEvents();
base.Init(manager, logPanel, loadingView);
m_boundManager = m_manager;
m_faceStateHandler = state =>
{
m_faceStateText.text = FaceProtocolEncoding.FaceStateMessage(state.State);
m_poseText.text = $"Yaw/Pitch/Roll: {state.Yaw}/{state.Pitch}/{state.Roll}";
};
m_manager.OnFaceStateUpdated += m_faceStateHandler;
}
private void OnDestroy()
{
UnbindManagerEvents();
}
/// 开始录入。
///
/// 执行 StartEnroll 方法。
///
public void StartEnroll()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
if (FaceProtocolEncoding.GetUserNameByteCount(ReadText(m_nameInput)) > FaceModuleConstants.UserNameSize)
{
SetResult("用户名 UTF-8 编码超过 32 bytes");
return;
}
await m_manager.EnsureExclusiveOperationReadyAsync("人脸录入");
FaceEnrollResult result;
if (m_modeDropdown.value == 1)
{
result = await m_manager.Enrollment.EnrollSingleAsync(m_adminToggle.isOn, ReadText(m_nameInput), ReadByte(m_timeoutInput, 20));
}
else if (m_modeDropdown.value == 2)
{
result = await m_manager.Enrollment.EnrollIntegratedAsync(m_adminToggle.isOn, ReadText(m_nameInput), Direction(), ReadByte(m_timeoutInput, 20), m_enrollTypeDropdown.value == 1, !m_duplicateToggle.isOn);
}
else
{
result = await m_manager.Enrollment.EnrollAsync(m_adminToggle.isOn, ReadText(m_nameInput), Direction(), ReadByte(m_timeoutInput, 20));
}
m_userIdText.text = "最终 UserID: " + result.UserId;
m_directionText.text = "已录入方向: " + DirectionText(result.Direction);
if (result.Success)
{
await ConfirmEnrollResult(result.UserId);
}
else
{
SetResult(FaceProtocolEncoding.ResultMessage(result.ResultCode));
}
});
}
///
/// 录入成功后读取用户信息,确认模组已写入用户表。
///
private async System.Threading.Tasks.Task ConfirmEnrollResult(int userId)
{
try
{
var info = await m_manager.Users.GetUserInfoAsync(userId);
SetResult("录入成功,已确认用户: ID=" + info.UserId + " 姓名=" + info.UserName + " 管理员=" + info.IsAdmin);
}
catch (Exception ex)
{
SetResult("录入成功,但查询用户确认失败: " + ex.Message);
}
}
/// 取消录入。
///
/// 执行 CancelEnroll 方法。
///
public void CancelEnroll()
{
RunAsync(async () => { if (RequireConnected()) { await m_manager.EnsureExclusiveOperationReadyAsync("取消录入"); await m_manager.Basic.FaceResetAsync(); SetResult("已取消录入"); } });
}
///
/// 执行 Direction 方法。
///
private FaceDirection Direction()
{
switch (m_directionDropdown.value)
{
case 1: return FaceDirection.Up;
case 2: return FaceDirection.Down;
case 3: return FaceDirection.Left;
case 4: return FaceDirection.Right;
default: return FaceDirection.Middle;
}
}
///
/// 执行 DirectionText 方法。
///
private string DirectionText(FaceDirection direction)
{
return direction.ToString();
}
private void UnbindManagerEvents()
{
if (m_boundManager != null && m_faceStateHandler != null)
{
m_boundManager.OnFaceStateUpdated -= m_faceStateHandler;
}
m_boundManager = null;
m_faceStateHandler = null;
}
}
public sealed class FaceUserManagePage : FaceModulePageBase
{
// 内部状态字段。
private InputField m_userIdInput;
// 内部状态字段。
private Text m_listText;
// 内部状态字段。
private Text m_detailText;
// 内部状态字段。
private FaceModuleConfirmDialog m_confirmDialog;
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "用户管理", 30);
m_userIdInput = builder.Input(transform, "UserID", "1");
var row = builder.Row(transform);
builder.Button(row.transform, "查询用户", QueryUser);
builder.Button(row.transform, "删除用户", DeleteUser);
builder.Button(row.transform, "删除全部", DeleteAll);
var row2 = builder.Row(transform);
builder.Button(row2.transform, "获取所有 ID", QueryAll);
builder.Button(row2.transform, "获取所有 ID2", QueryAll2);
var scroll = builder.ScrollView(transform, out m_listText);
scroll.GetComponent().preferredHeight = 240;
m_detailText = builder.Text(transform, "用户详情: -");
m_resultText = builder.Text(transform, "结果: -");
}
public override void Init(FaceModuleManager manager, FaceModuleLogPanel logPanel, FaceModuleLoadingView loadingView)
{
base.Init(manager, logPanel, loadingView);
m_confirmDialog = FindObjectOfType(true);
}
///
/// 执行 QueryUser 方法。
///
public void QueryUser()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.EnsureExclusiveOperationReadyAsync("查询用户");
var info = await m_manager.Users.GetUserInfoAsync(ReadInt(m_userIdInput, 0));
m_detailText.text = $"用户详情: ID={info.UserId} Name={info.UserName} Admin={info.IsAdmin}";
SetResult("查询成功");
});
}
///
/// 执行 DeleteUser 方法。
///
public void DeleteUser()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.EnsureExclusiveOperationReadyAsync("删除用户");
await m_manager.Users.DeleteUserAsync(ReadInt(m_userIdInput, 0));
SetResult("删除成功");
await RefreshIds(false);
});
}
///
/// 执行 DeleteAll 方法。
///
public void DeleteAll()
{
if (m_confirmDialog != null)
{
m_confirmDialog.Show("确定删除全部用户?该操作不可撤销。", DeleteAllConfirmed);
return;
}
DeleteAllConfirmed();
}
///
/// 执行 DeleteAllConfirmed 方法。
///
private void DeleteAllConfirmed()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.EnsureExclusiveOperationReadyAsync("删除全部用户");
await m_manager.Users.DeleteAllAsync();
SetResult("全部用户已删除");
await RefreshIds(false);
});
}
///
/// 执行 QueryAll 方法。
///
public void QueryAll()
{
RunAsync(async () => { if (RequireReady()) await RefreshIds(false); });
}
///
/// 执行 QueryAll2 方法。
///
public void QueryAll2()
{
RunAsync(async () => { if (RequireReady()) await RefreshIds(true); });
}
///
/// 执行 RefreshIds 方法。
///
private async System.Threading.Tasks.Task RefreshIds(bool useV2)
{
await m_manager.EnsureExclusiveOperationReadyAsync("刷新用户列表");
var ids = useV2 ? await m_manager.Users.GetAllUserIds2Async() : await m_manager.Users.GetAllUserIdsAsync();
m_listText.text = string.Join("\n", ids.ConvertAll(id => "UserID: " + id));
SetResult("用户列表已刷新");
}
}
public sealed class FaceQrCodePage : FaceModulePageBase
{
// 内部状态字段。
private InputField m_timeoutInput;
// 内部状态字段。
private Text m_qrText;
private FaceModuleManager m_boundManager; // 已订阅事件的 Manager。
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "二维码", 30);
m_timeoutInput = builder.Input(transform, "扫码超时时间", "10");
var row = builder.Row(transform);
builder.Button(row.transform, "开始扫码", Scan);
builder.Button(row.transform, "自动探测二维码", AutoScan);
builder.Button(row.transform, "停止", Stop);
builder.Button(row.transform, "复制内容", Copy);
m_qrText = builder.Text(transform, "二维码内容: -", 22, TextAnchor.UpperLeft);
m_resultText = builder.Text(transform, "扫码状态: -");
}
public override void Init(FaceModuleManager manager, FaceModuleLogPanel logPanel, FaceModuleLoadingView loadingView)
{
UnbindManagerEvents();
base.Init(manager, logPanel, loadingView);
m_boundManager = m_manager;
m_manager.OnQrCodeReceived += OnQrCodeReceived;
}
private void OnDestroy()
{
UnbindManagerEvents();
}
///
/// 执行 Scan 方法。
///
public void Scan()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.EnsureExclusiveOperationReadyAsync("扫码");
var qr = await m_manager.QrCode.ScanQrCodeAsync(ReadByte(m_timeoutInput, 10));
m_qrText.text = "二维码内容: " + qr;
SetResult(string.IsNullOrEmpty(qr) ? "二维码为空" : "扫码成功");
});
}
///
/// 执行 AutoScan 方法。
///
public void AutoScan()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.StartAutoVerifyAsync(ReadByte(m_timeoutInput, 10)); SetResult("自动探测已启动"); } });
}
///
/// 执行 Stop 方法。
///
public void Stop()
{
RunAsync(async () =>
{
if (!RequireConnected()) return;
if (m_manager.IsAutoVerifyEnabled)
{
await m_manager.StopAutoVerifyAsync();
SetResult("自动探测已停止");
return;
}
await m_manager.Basic.ResetAsync();
SetResult("已停止");
});
}
///
/// 执行 Copy 方法。
///
public void Copy()
{
GUIUtility.systemCopyBuffer = m_qrText.text.Replace("二维码内容: ", string.Empty);
SetResult("已复制到剪贴板");
}
private void OnQrCodeReceived(string qr)
{
m_qrText.text = "二维码内容: " + qr;
}
private void UnbindManagerEvents()
{
if (m_boundManager != null)
{
m_boundManager.OnQrCodeReceived -= OnQrCodeReceived;
m_boundManager = null;
}
}
}
public sealed class FaceImagePage : FaceModulePageBase
{
// 内部状态字段。
private Dropdown m_dpiDropdown;
// 内部状态字段。
private Slider m_progress;
// 内部状态字段。
private Text m_sizeText;
// 内部状态字段。
private RawImage m_preview;
// 内部状态字段。
private Text m_userIdText;
// 内部状态字段。
private byte[] m_lastBytes;
// 内部状态字段。
private string m_lastType;
public override void Build(FaceModuleUiBuilder builder)
{
builder.Text(transform, "图片抓拍", 30);
m_dpiDropdown = builder.Dropdown(transform, new[] { "480x640", "600x800", "1200x1600" });
var row = builder.Row(transform);
builder.Button(row.transform, "抓拍普通图片", () => Snap("Normal"));
builder.Button(row.transform, "抓拍人脸图片", () => Snap("Face"));
builder.Button(row.transform, "抓拍大图", () => Snap("Large"));
builder.Button(row.transform, "保存图片", SaveImage);
builder.Button(row.transform, "清空图片", ClearImage);
m_progress = builder.Slider(transform);
m_sizeText = builder.Text(transform, "图片大小: -");
m_userIdText = builder.Text(transform, "本地匹配 UserID: -");
var previewGo = FaceModuleUiBuilder.Create("Preview", transform);
previewGo.AddComponent().preferredHeight = 360;
previewGo.AddComponent().color = Color.white;
m_preview = FaceModuleUiBuilder.Create("RawImage", previewGo.transform).AddComponent();
FaceModuleUiBuilder.Stretch(m_preview.rectTransform);
m_resultText = builder.Text(transform, "结果: -");
}
///
/// 执行 Snap 方法。
///
public void Snap(string type)
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.EnsureExclusiveOperationReadyAsync("图片抓拍");
FaceImageResult result;
if (type == "Face") result = await m_manager.Images.SnapUploadFaceImageAsync();
else if (type == "Large") result = await m_manager.Images.SnapUploadLargeImageAsync((ImageDpi)m_dpiDropdown.value);
else result = await m_manager.Images.SnapUploadImageAsync();
m_lastBytes = result.Bytes;
m_lastType = type;
m_preview.texture = result.Texture;
m_sizeText.text = "图片大小: " + (m_lastBytes == null ? 0 : m_lastBytes.Length) + " bytes";
m_userIdText.text = "本地匹配 UserID: " + result.UserId;
m_progress.value = 1;
SetResult(result.Bytes.Length > 0 ? "图片接收完成" : "抓拍命令已完成,未收到图片数据");
});
}
///
/// 执行 SaveImage 方法。
///
public void SaveImage()
{
if (m_lastBytes == null || m_lastBytes.Length == 0)
{
SetResult("没有可保存的图片");
return;
}
var dir = Path.Combine(Application.persistentDataPath, "FaceModuleImages");
Directory.CreateDirectory(dir);
File.WriteAllBytes(Path.Combine(dir, DateTime.Now.ToString("yyyyMMdd_HHmmss_") + m_lastType + ImageExtension(m_lastBytes)), m_lastBytes);
SetResult("图片已保存: " + dir);
}
///
/// 执行 ClearImage 方法。
///
public void ClearImage()
{
m_lastBytes = null;
m_preview.texture = null;
m_sizeText.text = "图片大小: -";
m_userIdText.text = "本地匹配 UserID: -";
m_progress.value = 0;
SetResult("已清空图片");
}
private static string ImageExtension(byte[] bytes)
{
if (bytes != null && bytes.Length >= 4 && bytes[0] == 0x89 && bytes[1] == 0x50 && bytes[2] == 0x4E && bytes[3] == 0x47)
{
return ".png";
}
return ".jpg";
}
}
}