si-m1-face-package/Samples/SI-M1 Face Demo/UI/Pages/FaceModuleAdvancedPages.cs

512 lines
20 KiB
C#

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, "注册结果: -");
}
/// <summary>刷新文件大小显示。</summary>
/// <summary>
/// 执行 RefreshFileInfo 方法。
/// </summary>
public void RefreshFileInfo()
{
var path = ReadText(m_filePathInput);
m_fileSizeText.text = File.Exists(path) ? "文件大小: " + new FileInfo(path).Length + " bytes" : "文件不存在";
}
/// <summary>使用 0xF7 注册。</summary>
/// <summary>
/// 执行 EnrollWithPhoto 方法。
/// </summary>
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);
});
}
/// <summary>使用 0xD7 指定 ID 注册。</summary>
/// <summary>
/// 执行 EnrollWithPhotoAndId 方法。
/// </summary>
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);
});
}
/// <summary>
/// 执行 ReadValidFile 方法。
/// </summary>
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;
}
/// <summary>
/// 执行 OnProgress 方法。
/// </summary>
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);
}
/// <summary>
/// 执行 ShowEnrollResult 方法。
/// </summary>
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, "操作结果: -");
}
/// <summary>读取特征。</summary>
/// <summary>
/// 执行 ReadFeature 方法。
/// </summary>
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 ? "特征读取完成" : "读取命令已完成,未收到特征数据");
});
}
/// <summary>保存特征文件。</summary>
/// <summary>
/// 执行 SaveFeature 方法。
/// </summary>
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);
}
/// <summary>写入特征。</summary>
/// <summary>
/// 执行 WriteFeature 方法。
/// </summary>
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));
});
}
/// <summary>
/// 执行 FeatureTypeValue 方法。
/// </summary>
private FeatureType FeatureTypeValue()
{
return (FeatureType)(m_featureTypeDropdown.value + 1);
}
/// <summary>
/// 执行 OnProgress 方法。
/// </summary>
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, "操作结果: -");
}
/// <summary>
/// 执行 ReadUvc 方法。
/// </summary>
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 参数读取成功");
});
}
/// <summary>
/// 执行 SetUvc 方法。
/// </summary>
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 参数设置成功");
});
}
/// <summary>
/// 执行 SetFaceBox 方法。
/// </summary>
public void SetFaceBox()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetFaceLocationDisplayAsync(m_faceBoxToggle.isOn); SetResult("人脸框显示设置成功"); } });
}
/// <summary>
/// 执行 UsbTypeValue 方法。
/// </summary>
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, "操作结果: -");
}
/// <summary>
/// 执行 SetRgb 方法。
/// </summary>
public void SetRgb()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetRgbLevelAsync((byte)m_rgbDropdown.value); SetResult("RGB_LEVEL 设置成功"); } });
}
/// <summary>
/// 执行 SetDuplicate 方法。
/// </summary>
public void SetDuplicate()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.Uvc.SetDuplicateCheckAsync(m_duplicateToggle.isOn); SetResult("查重设置成功"); } });
}
/// <summary>
/// 执行 ReadDuplicate 方法。
/// </summary>
public void ReadDuplicate()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
m_duplicateToggle.isOn = await m_manager.Uvc.ReadDuplicateCheckAsync();
SetResult("查重状态已读取");
});
}
/// <summary>
/// 执行 SetDemoMode 方法。
/// </summary>
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, "操作结果: -");
}
/// <summary>
/// 执行 SetReleaseKey 方法。
/// </summary>
public void SetReleaseKey()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.Encryption.SetReleaseEncryptionKeyAsync(ParseHex(m_releaseKeyInput.text, 16)); SetResult("Release Key 设置成功"); } });
}
/// <summary>
/// 执行 SetDebugKey 方法。
/// </summary>
public void SetDebugKey()
{
RunAsync(async () => { if (RequireReady()) { await m_manager.Encryption.SetDebugEncryptionKeyAsync(ParseHex(m_debugKeyInput.text, 16)); SetResult("Debug Key 设置成功"); } });
}
/// <summary>
/// 执行 GenerateSeed 方法。
/// </summary>
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 已生成");
}
/// <summary>
/// 执行 InitEncryption 方法。
/// </summary>
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 完成");
});
}
/// <summary>
/// 执行 ParseHex 方法。
/// </summary>
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, "操作结果: -");
}
/// <summary>启动 U 盘固件升级。</summary>
/// <summary>
/// 执行 StartUpgrade 方法。
/// </summary>
public void StartUpgrade()
{
RunAsync(async () =>
{
if (!RequireReady()) return;
await m_manager.Basic.UpgradeFirmwareAsync();
m_progress.value = 1;
m_percentText.text = "当前进度: 已启动";
SetResult("固件升级命令已发送");
});
}
}
}