using System; using System.IO; 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 FacePhotoEnrollPage : FaceModulePageBase { // 内部状态字段。 private InputField m_filePathInput; // 内部状态字段。 private Text m_fileSizeText; // 内部状态字段。 private Dropdown m_bioTypeDropdown; // 内部状态字段。 private InputField m_userNameInput; // 内部状态字段。 private Toggle m_duplicateToggle; // 内部状态字段。 private Toggle m_specifyUserIdToggle; // 内部状态字段。 private InputField m_userIdInput; // 内部状态字段。 private Slider m_progress; // 内部状态字段。 private Text m_seqText; // 内部状态字段。 private Text m_userIdText; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "照片 / 特征注册", 30); m_filePathInput = builder.Input(transform, "文件路径", string.Empty); var row = builder.Row(transform); builder.Button(row.transform, "刷新文件信息", RefreshFileInfo); builder.Button(row.transform, "使用 0xF7 注册", EnrollWithPhoto); builder.Button(row.transform, "使用 0xD7 指定 ID 注册", EnrollWithPhotoAndId); m_fileSizeText = builder.Text(transform, "文件大小: -"); m_bioTypeDropdown = builder.Dropdown(transform, new[] { "普通照片", "加密照片", "普通特征码", "压缩特征码" }); m_userNameInput = builder.Input(transform, "用户姓名", "User"); m_duplicateToggle = builder.Toggle(transform, "启用查重", true); m_specifyUserIdToggle = builder.Toggle(transform, "指定 UserID", false); m_userIdInput = builder.Input(transform, "指定 UserID", "1"); m_progress = builder.Slider(transform); m_seqText = builder.Text(transform, "当前 Seq: -"); m_userIdText = builder.Text(transform, "注册 UserID: -"); m_resultText = builder.Text(transform, "注册结果: -"); } /// 刷新文件大小显示。 /// /// 执行 RefreshFileInfo 方法。 /// public void RefreshFileInfo() { var path = ReadText(m_filePathInput); m_fileSizeText.text = File.Exists(path) ? "文件大小: " + new FileInfo(path).Length + " bytes" : "文件不存在"; } /// 使用 0xF7 注册。 /// /// 执行 EnrollWithPhoto 方法。 /// public void EnrollWithPhoto() { RunAsync(async () => { if (!RequireReady()) return; var bytes = ReadValidFile(); var result = await m_manager.Images.EnrollWithPhotoAsync(bytes, (BioType)m_bioTypeDropdown.value, ReadText(m_userNameInput), m_duplicateToggle.isOn, OnProgress); ShowEnrollResult(result); }); } /// 使用 0xD7 指定 ID 注册。 /// /// 执行 EnrollWithPhotoAndId 方法。 /// public void EnrollWithPhotoAndId() { RunAsync(async () => { if (!RequireReady()) return; var bytes = ReadValidFile(); var userId = ReadInt(m_userIdInput, 0); if (!m_specifyUserIdToggle.isOn) { SetResult("请先启用指定 UserID"); return; } var result = await m_manager.Images.EnrollWithPhotoAndIdAsync(userId, bytes, (BioType)m_bioTypeDropdown.value, ReadText(m_userNameInput), m_duplicateToggle.isOn, OnProgress); ShowEnrollResult(result); }); } /// /// 执行 ReadValidFile 方法。 /// private byte[] ReadValidFile() { var path = ReadText(m_filePathInput); if (!File.Exists(path)) throw new FileNotFoundException("文件不存在", path); var bytes = File.ReadAllBytes(path); if (bytes.Length == 0) throw new InvalidOperationException("文件为空"); if (bytes.Length > 8 * 1024 * 1024) throw new InvalidOperationException("文件过大,建议小于 8MB"); return bytes; } /// /// 执行 OnProgress 方法。 /// private void OnProgress(int sent, int total) { m_progress.value = total > 0 ? (float)sent / total : 0; m_seqText.text = "当前 Seq: " + Mathf.CeilToInt((float)sent / FaceModuleConstants.PhotoRegisterMtu); } /// /// 执行 ShowEnrollResult 方法。 /// private void ShowEnrollResult(FaceEnrollResult result) { m_userIdText.text = "注册 UserID: " + result.UserId; SetResult(result.Success ? "注册成功" : FaceProtocolEncoding.ResultMessage(result.ResultCode)); } } public sealed class FaceFeaturePage : FaceModulePageBase { // 内部状态字段。 private InputField m_userIdInput; // 内部状态字段。 private Dropdown m_featureTypeDropdown; // 内部状态字段。 private InputField m_filePathInput; // 内部状态字段。 private InputField m_userNameInput; // 内部状态字段。 private Slider m_progress; // 内部状态字段。 private Text m_seqText; // 内部状态字段。 private Text m_lengthText; // 内部状态字段。 private byte[] m_lastFeature; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "特征管理", 30); m_userIdInput = builder.Input(transform, "UserID", "1"); m_featureTypeDropdown = builder.Dropdown(transform, new[] { "彩色人脸模板", "彩色 + 红外人脸模板", "掌纹掌静脉模板" }); m_filePathInput = builder.Input(transform, "特征文件路径", string.Empty); m_userNameInput = builder.Input(transform, "用户姓名", "FeatureUser"); var row = builder.Row(transform); builder.Button(row.transform, "读取特征", ReadFeature); builder.Button(row.transform, "保存特征文件", SaveFeature); builder.Button(row.transform, "写入特征", WriteFeature); m_progress = builder.Slider(transform); m_seqText = builder.Text(transform, "当前 Seq: -"); m_lengthText = builder.Text(transform, "特征长度: -"); m_resultText = builder.Text(transform, "操作结果: -"); } /// 读取特征。 /// /// 执行 ReadFeature 方法。 /// public void ReadFeature() { RunAsync(async () => { if (!RequireReady()) return; m_lastFeature = await m_manager.Features.ReadFeatureAsync(ReadInt(m_userIdInput, 0), FeatureTypeValue()); m_lengthText.text = "特征长度: " + (m_lastFeature == null ? 0 : m_lastFeature.Length); SetResult(m_lastFeature.Length > 0 ? "特征读取完成" : "读取命令已完成,未收到特征数据"); }); } /// 保存特征文件。 /// /// 执行 SaveFeature 方法。 /// public void SaveFeature() { if (m_lastFeature == null || m_lastFeature.Length == 0) { SetResult("没有可保存的特征数据"); return; } var dir = Path.Combine(Application.persistentDataPath, "FaceModuleFeatures"); Directory.CreateDirectory(dir); var name = $"{DateTime.Now:yyyyMMdd_HHmmss}_User_{ReadInt(m_userIdInput, 0)}_Feature_{FeatureTypeValue()}.bin"; File.WriteAllBytes(Path.Combine(dir, name), m_lastFeature); SetResult("特征已保存: " + dir); } /// 写入特征。 /// /// 执行 WriteFeature 方法。 /// public void WriteFeature() { RunAsync(async () => { if (!RequireReady()) return; var path = ReadText(m_filePathInput); if (!File.Exists(path)) throw new FileNotFoundException("特征文件不存在", path); var bytes = File.ReadAllBytes(path); var result = await m_manager.Features.WriteFeatureAsync(bytes, FeatureTypeValue(), ReadText(m_userNameInput), OnProgress); SetResult(result.Success ? "写入成功 UserID=" + result.UserId : FaceProtocolEncoding.ResultMessage(result.ResultCode)); }); } /// /// 执行 FeatureTypeValue 方法。 /// private FeatureType FeatureTypeValue() { return (FeatureType)(m_featureTypeDropdown.value + 1); } /// /// 执行 OnProgress 方法。 /// private void OnProgress(int sent, int total) { m_progress.value = total > 0 ? (float)sent / total : 0; m_seqText.text = "当前 Seq: " + Mathf.CeilToInt((float)sent / FaceModuleConstants.FeaturePacketSize); m_lengthText.text = "特征长度: " + total; } } public sealed class FaceUvcSettingsPage : FaceModulePageBase { // 内部状态字段。 private Dropdown m_usbTypeDropdown; // 内部状态字段。 private InputField m_bitrateInput; // 内部状态字段。 private InputField m_qualityInput; // 内部状态字段。 private Toggle m_mirrorToggle; // 内部状态字段。 private Toggle m_rotateToggle; // 内部状态字段。 private Toggle m_faceBoxToggle; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "UVC 设置", 30); m_usbTypeDropdown = builder.Dropdown(transform, new[] { "USB1.1 Bulk 0x11", "USB2.0 Bulk 0x20", "USB1.1 ISOC 0x91", "USB2.0 ISOC 0xA0" }); m_bitrateInput = builder.Input(transform, "UVC 码率 Mbps", "8"); m_qualityInput = builder.Input(transform, "MJPG 质量 10-100", "80"); m_mirrorToggle = builder.Toggle(transform, "镜像", false); m_rotateToggle = builder.Toggle(transform, "180 度倒转", false); m_faceBoxToggle = builder.Toggle(transform, "显示人脸框", true); var row = builder.Row(transform); builder.Button(row.transform, "读取 UVC 参数", ReadUvc); builder.Button(row.transform, "设置 UVC 参数", SetUvc); builder.Button(row.transform, "设置人脸框显示", SetFaceBox); m_resultText = builder.Text(transform, "操作结果: -"); } /// /// 执行 ReadUvc 方法。 /// public void ReadUvc() { RunAsync(async () => { if (!RequireReady()) return; var p = await m_manager.Uvc.ReadUsbUvcParametersAsync(); m_bitrateInput.text = p.BitrateMbps.ToString(); m_qualityInput.text = p.MjpgQuality.ToString(); m_mirrorToggle.isOn = p.Mirror; m_rotateToggle.isOn = p.Rotate180; SetResult("UVC 参数读取成功"); }); } /// /// 执行 SetUvc 方法。 /// public void SetUvc() { RunAsync(async () => { if (!RequireReady()) return; var bitrate = ReadByte(m_bitrateInput, 8); var quality = Mathf.Clamp(ReadByte(m_qualityInput, 80), 10, 100); await m_manager.Uvc.SetUsbUvcParametersAsync(new UsbUvcParameters { UsbType = UsbTypeValue(), BitrateMbps = bitrate, MjpgQuality = (byte)quality, Mirror = m_mirrorToggle.isOn, Rotate180 = m_rotateToggle.isOn }); SetResult("UVC 参数设置成功"); }); } /// /// 执行 SetFaceBox 方法。 /// public void SetFaceBox() { RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetFaceLocationDisplayAsync(m_faceBoxToggle.isOn); SetResult("人脸框显示设置成功"); } }); } /// /// 执行 UsbTypeValue 方法。 /// private byte UsbTypeValue() { switch (m_usbTypeDropdown.value) { case 0: return 0x11; case 1: return 0x20; case 2: return 0x91; default: return 0xA0; } } } public sealed class FaceAdvancedSettingsPage : FaceModulePageBase { // 内部状态字段。 private Dropdown m_rgbDropdown; // 内部状态字段。 private Toggle m_duplicateToggle; // 内部状态字段。 private Toggle m_demoToggle; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "高级设置", 30); m_rgbDropdown = builder.Dropdown(transform, new[] { "0", "1", "2 默认", "3", "4" }); m_rgbDropdown.value = 2; m_duplicateToggle = builder.Toggle(transform, "启用查重", true); m_demoToggle = builder.Toggle(transform, "Demo Mode: 所有人可解锁,但仍做活体检测", false); var row = builder.Row(transform); builder.Button(row.transform, "设置 RGB_LEVEL", SetRgb); builder.Button(row.transform, "设置查重", SetDuplicate); builder.Button(row.transform, "读取查重状态", ReadDuplicate); builder.Button(row.transform, "设置 Demo Mode", SetDemoMode); m_resultText = builder.Text(transform, "操作结果: -"); } /// /// 执行 SetRgb 方法。 /// public void SetRgb() { RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetRgbLevelAsync((byte)m_rgbDropdown.value); SetResult("RGB_LEVEL 设置成功"); } }); } /// /// 执行 SetDuplicate 方法。 /// public void SetDuplicate() { RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetDuplicateCheckAsync(m_duplicateToggle.isOn); SetResult("查重设置成功"); } }); } /// /// 执行 ReadDuplicate 方法。 /// public void ReadDuplicate() { RunAsync(async () => { if (!RequireReady()) return; m_duplicateToggle.isOn = await m_manager.Uvc.ReadDuplicateCheckAsync(); SetResult("查重状态已读取"); }); } /// /// 执行 SetDemoMode 方法。 /// public void SetDemoMode() { RunAsync(async () => { if (RequireReady()) { await m_manager.Basic.SetDemoModeAsync(m_demoToggle.isOn); SetResult("Demo Mode 设置成功"); } }); } } public sealed class FaceEncryptionPage : FaceModulePageBase { // 内部状态字段。 private InputField m_releaseKeyInput; // 内部状态字段。 private InputField m_debugKeyInput; // 内部状态字段。 private InputField m_seedInput; // 内部状态字段。 private Text m_stateText; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "加密通信", 30); m_releaseKeyInput = builder.Input(transform, "Release Key 16 bytes Hex", string.Empty); m_debugKeyInput = builder.Input(transform, "Debug Key 16 bytes Hex", string.Empty); m_seedInput = builder.Input(transform, "Seed 4 bytes Hex", string.Empty); var row = builder.Row(transform); builder.Button(row.transform, "设置 Release Key", SetReleaseKey); builder.Button(row.transform, "设置 Debug Key", SetDebugKey); builder.Button(row.transform, "生成随机 Seed", GenerateSeed); builder.Button(row.transform, "Init Encryption", InitEncryption); m_stateText = builder.Text(transform, "当前加密状态: 加密算法未接入,当前仅完成协议入口"); m_resultText = builder.Text(transform, "操作结果: -"); } /// /// 执行 SetReleaseKey 方法。 /// public void SetReleaseKey() { RunAsync(async () => { if (RequireReady()) { await m_manager.Encryption.SetReleaseEncryptionKeyAsync(ParseHex(m_releaseKeyInput.text, 16)); SetResult("Release Key 设置成功"); } }); } /// /// 执行 SetDebugKey 方法。 /// public void SetDebugKey() { RunAsync(async () => { if (RequireReady()) { await m_manager.Encryption.SetDebugEncryptionKeyAsync(ParseHex(m_debugKeyInput.text, 16)); SetResult("Debug Key 设置成功"); } }); } /// /// 执行 GenerateSeed 方法。 /// public void GenerateSeed() { var bytes = new byte[4]; using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider()) { rng.GetBytes(bytes); } m_seedInput.text = BitConverter.ToString(bytes).Replace("-", " "); SetResult("Seed 已生成"); } /// /// 执行 InitEncryption 方法。 /// public void InitEncryption() { RunAsync(async () => { if (!RequireReady()) return; await m_manager.Encryption.InitEncryptionAsync(ParseHex(m_seedInput.text, 4), 0); m_stateText.text = "当前加密状态: 协议入口已调用,供应商算法仍未接入"; SetResult("Init Encryption 完成"); }); } /// /// 执行 ParseHex 方法。 /// private static byte[] ParseHex(string text, int expectedLength) { var parts = text.Split(new[] { ' ', '-', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != expectedLength) throw new ArgumentException("需要 " + expectedLength + " bytes Hex"); var bytes = new byte[parts.Length]; for (var i = 0; i < parts.Length; i++) bytes[i] = Convert.ToByte(parts[i], 16); return bytes; } } public sealed class FaceFirmwarePage : FaceModulePageBase { // 内部状态字段。 private Slider m_progress; // 内部状态字段。 private Text m_percentText; public override void Build(FaceModuleUiBuilder builder) { builder.Text(transform, "固件升级", 30); builder.Text(transform, "风险提示: 升级过程中不要断电;固件文件应由 U 盘或模组侧准备。", 22); builder.Button(transform, "启动 U 盘升级", StartUpgrade); m_progress = builder.Slider(transform); m_percentText = builder.Text(transform, "当前进度: 0%"); m_resultText = builder.Text(transform, "操作结果: -"); } /// 启动 U 盘固件升级。 /// /// 执行 StartUpgrade 方法。 /// public void StartUpgrade() { RunAsync(async () => { if (!RequireReady()) return; await m_manager.Basic.UpgradeFirmwareAsync(); m_progress.value = 1; m_percentText.text = "当前进度: 已启动"; SetResult("固件升级命令已发送"); }); } } }