diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19aba6a..f993d79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.1.6
+
+- Added `FaceModuleManager` singleton lifecycle validation.
+- Added default `FaceSdkSettings` loading from `Resources/AISU/SI-M1 Face SDK/FaceSdkSettings`.
+- Added auto verify state, start/stop APIs, auto verify result event dispatch, and exclusive operation conflict handling.
+- Added auto verify configuration fields to `FaceSdkSettings`.
+- Limited Editor menu entries to Settings and License.
+- Updated samples to use explicit auto verify start/stop and exclusive operation preparation.
+
## 0.1.5
- Fixed Unity 2021 compatibility for the obfuscated Runtime and Editor DLLs by removing invalid `System.Private.CoreLib` assembly references introduced during obfuscation.
diff --git a/Editor/Aisu.SIM1.Face.Editor.dll b/Editor/Aisu.SIM1.Face.Editor.dll
index e2ef8c4..3588558 100644
Binary files a/Editor/Aisu.SIM1.Face.Editor.dll and b/Editor/Aisu.SIM1.Face.Editor.dll differ
diff --git a/Resources.meta b/Resources.meta
new file mode 100644
index 0000000..6ed41eb
--- /dev/null
+++ b/Resources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 38a8adf823e04ed4ba6e7819d0d6f001
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/AISU.meta b/Resources/AISU.meta
new file mode 100644
index 0000000..76756fd
--- /dev/null
+++ b/Resources/AISU.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 38a8adf823e04ed4ba6e7819d0d6f002
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/AISU/SI-M1 Face SDK.meta b/Resources/AISU/SI-M1 Face SDK.meta
new file mode 100644
index 0000000..b619310
--- /dev/null
+++ b/Resources/AISU/SI-M1 Face SDK.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 38a8adf823e04ed4ba6e7819d0d6f003
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset b/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset
new file mode 100644
index 0000000..96e153c
--- /dev/null
+++ b/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset
@@ -0,0 +1,31 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 8f79a6ea36b743d49ac4f2de7f2ea81f, type: 3}
+ m_Name: FaceSdkSettings
+ m_EditorClassIdentifier:
+ m_transportMode: 0
+ m_useMockInEditor: 0
+ m_portName: COM3
+ m_baudRate: 115200
+ m_readBufferSize: 4096
+ m_useEditorSerialDebugParameters: 1
+ m_editorPortName: COM3
+ m_editorBaudRate: 115200
+ m_editorReadBufferSize: 4096
+ m_mockResponseDelayFrames: 1
+ m_autoVerifyOnOpen: 0
+ m_autoVerifyTimeout: 10
+ m_stopAutoVerifyBeforeExclusiveCommand: 1
+ m_exclusiveCommandPolicy: 1
+ m_debugLogEnabled: 1
+ m_rawLogEnabled: 1
+ m_logPrefix: '[FaceModule]'
diff --git a/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset.meta b/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset.meta
new file mode 100644
index 0000000..806aaa7
--- /dev/null
+++ b/Resources/AISU/SI-M1 Face SDK/FaceSdkSettings.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 38a8adf823e04ed4ba6e7819d0d6f004
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Aisu.SIM1.Face.Runtime.dll b/Runtime/Aisu.SIM1.Face.Runtime.dll
index ff7671c..e2be0d4 100644
Binary files a/Runtime/Aisu.SIM1.Face.Runtime.dll and b/Runtime/Aisu.SIM1.Face.Runtime.dll differ
diff --git a/Samples~/Face Detection Demo/Scripts/FaceDetectionDemoController.cs b/Samples~/Face Detection Demo/Scripts/FaceDetectionDemoController.cs
index e021961..faa0335 100644
--- a/Samples~/Face Detection Demo/Scripts/FaceDetectionDemoController.cs
+++ b/Samples~/Face Detection Demo/Scripts/FaceDetectionDemoController.cs
@@ -64,6 +64,7 @@ namespace Aisu.SIM1.Face.Samples.FaceDetectionDemo
m_manager.OnError += OnManagerError;
m_manager.OnReady += OnManagerReady;
m_manager.OnFaceStateUpdated += OnFaceStateUpdated;
+ m_manager.OnAutoVerifyResult += OnAutoVerifyResult;
}
if (m_serialDropdown != null)
@@ -132,6 +133,7 @@ namespace Aisu.SIM1.Face.Samples.FaceDetectionDemo
m_manager.OnError -= OnManagerError;
m_manager.OnReady -= OnManagerReady;
m_manager.OnFaceStateUpdated -= OnFaceStateUpdated;
+ m_manager.OnAutoVerifyResult -= OnAutoVerifyResult;
m_manager.Close();
}
@@ -350,6 +352,17 @@ namespace Aisu.SIM1.Face.Samples.FaceDetectionDemo
SetResult("人脸状态: " + FaceProtocolEncoding.FaceStateMessage(state.State));
}
+ private void OnAutoVerifyResult(FaceAutoVerifyResult result)
+ {
+ if (result.Success)
+ {
+ SetResult("自动检测成功,UserID=" + result.VerifyResult.UserId + ",姓名=" + result.VerifyResult.UserName);
+ return;
+ }
+
+ SetResult("自动检测结果: " + FaceProtocolEncoding.ResultMessage(result.ResultCode));
+ }
+
private void OnManagerError(FaceResultCode _, string message)
{
LogError("SDK 错误: " + message);
@@ -383,6 +396,12 @@ namespace Aisu.SIM1.Face.Samples.FaceDetectionDemo
private async Task EnsureModuleStandbyAsync(string operationName)
{
+ if (m_manager.IsAutoVerifyEnabled)
+ {
+ await m_manager.EnsureExclusiveOperationReadyAsync(operationName);
+ return;
+ }
+
var status = await m_manager.Basic.GetStatusAsync();
if (status == FaceModuleStatus.Busy)
{
diff --git a/Samples~/SI-M1 Face Demo/Scripts/Core/FaceModuleKeyboardTester.cs b/Samples~/SI-M1 Face Demo/Scripts/Core/FaceModuleKeyboardTester.cs
index a244ffa..687c932 100644
--- a/Samples~/SI-M1 Face Demo/Scripts/Core/FaceModuleKeyboardTester.cs
+++ b/Samples~/SI-M1 Face Demo/Scripts/Core/FaceModuleKeyboardTester.cs
@@ -361,8 +361,15 @@ namespace Aisu.SIM1.Face.Core
{
try
{
- var result = await Manager.Verify.AutoVerifyAsync(AutoVerifyEnable, VerifyTimeout);
- Log("自动验证完成:成功=" + result.Success + ",用户ID=" + result.UserId);
+ if (AutoVerifyEnable)
+ {
+ await Manager.StartAutoVerifyAsync(VerifyTimeout);
+ Log("自动验证已开启,识别结果通过事件回调。");
+ return;
+ }
+
+ await Manager.StopAutoVerifyAsync(VerifyTimeout);
+ Log("自动验证已关闭。");
}
catch (Exception ex) { LogError("自动验证失败:" + ex.Message); }
}
diff --git a/Samples~/SI-M1 Face Demo/UI/Pages/FaceModulePages.cs b/Samples~/SI-M1 Face Demo/UI/Pages/FaceModulePages.cs
index 4530c3b..a5b8763 100644
--- a/Samples~/SI-M1 Face Demo/UI/Pages/FaceModulePages.cs
+++ b/Samples~/SI-M1 Face Demo/UI/Pages/FaceModulePages.cs
@@ -401,6 +401,7 @@ namespace Aisu.SIM1.Face.UI.Pages
base.Init(manager, logPanel, loadingView);
m_boundManager = m_manager;
m_manager.OnFaceStateUpdated += OnFaceState;
+ m_manager.OnAutoVerifyResult += OnAutoVerifyResult;
}
private void OnDestroy()
@@ -417,9 +418,14 @@ namespace Aisu.SIM1.Face.UI.Pages
RunAsync(async () =>
{
if (!RequireReady()) return;
- var result = m_autoToggle.isOn
- ? await m_manager.Verify.AutoVerifyAsync(true, ReadByte(m_timeoutInput, 10))
- : await m_manager.Verify.VerifyAsync(ReadByte(m_timeoutInput, 10), ReadByte(m_maxTimesInput, 30));
+ 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);
});
}
@@ -430,7 +436,19 @@ namespace Aisu.SIM1.Face.UI.Pages
///
public void StopVerify()
{
- RunAsync(async () => { if (RequireConnected()) { await m_manager.Basic.ResetAsync(); SetResult("已停止验证"); } });
+ RunAsync(async () =>
+ {
+ if (!RequireConnected()) return;
+ if (m_manager.IsAutoVerifyEnabled)
+ {
+ await m_manager.StopAutoVerifyAsync();
+ SetResult("自动验证已关闭");
+ return;
+ }
+
+ await m_manager.Basic.ResetAsync();
+ SetResult("已停止验证");
+ });
}
///
@@ -455,11 +473,17 @@ namespace Aisu.SIM1.Face.UI.Pages
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;
}
}
@@ -545,6 +569,8 @@ namespace Aisu.SIM1.Face.UI.Pages
return;
}
+ await m_manager.EnsureExclusiveOperationReadyAsync("人脸录入");
+
FaceEnrollResult result;
if (m_modeDropdown.value == 1)
{
@@ -594,7 +620,7 @@ namespace Aisu.SIM1.Face.UI.Pages
///
public void CancelEnroll()
{
- RunAsync(async () => { if (RequireConnected()) { await m_manager.Basic.FaceResetAsync(); SetResult("已取消录入"); } });
+ RunAsync(async () => { if (RequireConnected()) { await m_manager.EnsureExclusiveOperationReadyAsync("取消录入"); await m_manager.Basic.FaceResetAsync(); SetResult("已取消录入"); } });
}
///
@@ -674,6 +700,7 @@ namespace Aisu.SIM1.Face.UI.Pages
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("查询成功");
@@ -688,6 +715,7 @@ namespace Aisu.SIM1.Face.UI.Pages
RunAsync(async () =>
{
if (!RequireReady()) return;
+ await m_manager.EnsureExclusiveOperationReadyAsync("删除用户");
await m_manager.Users.DeleteUserAsync(ReadInt(m_userIdInput, 0));
SetResult("删除成功");
await RefreshIds(false);
@@ -716,6 +744,7 @@ namespace Aisu.SIM1.Face.UI.Pages
RunAsync(async () =>
{
if (!RequireReady()) return;
+ await m_manager.EnsureExclusiveOperationReadyAsync("删除全部用户");
await m_manager.Users.DeleteAllAsync();
SetResult("全部用户已删除");
await RefreshIds(false);
@@ -743,6 +772,7 @@ namespace Aisu.SIM1.Face.UI.Pages
///
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("用户列表已刷新");
@@ -791,6 +821,7 @@ namespace Aisu.SIM1.Face.UI.Pages
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) ? "二维码为空" : "扫码成功");
@@ -802,7 +833,7 @@ namespace Aisu.SIM1.Face.UI.Pages
///
public void AutoScan()
{
- RunAsync(async () => { if (RequireReady()) { await m_manager.Verify.AutoVerifyAsync(true, ReadByte(m_timeoutInput, 10)); SetResult("自动探测已启动"); } });
+ RunAsync(async () => { if (RequireReady()) { await m_manager.StartAutoVerifyAsync(ReadByte(m_timeoutInput, 10)); SetResult("自动探测已启动"); } });
}
///
@@ -810,7 +841,19 @@ namespace Aisu.SIM1.Face.UI.Pages
///
public void Stop()
{
- RunAsync(async () => { if (RequireConnected()) { await m_manager.Basic.ResetAsync(); SetResult("已停止"); } });
+ RunAsync(async () =>
+ {
+ if (!RequireConnected()) return;
+ if (m_manager.IsAutoVerifyEnabled)
+ {
+ await m_manager.StopAutoVerifyAsync();
+ SetResult("自动探测已停止");
+ return;
+ }
+
+ await m_manager.Basic.ResetAsync();
+ SetResult("已停止");
+ });
}
///
@@ -883,6 +926,7 @@ namespace Aisu.SIM1.Face.UI.Pages
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);
diff --git a/package.json b/package.json
index d4627dc..db315a9 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "xin.aisu.si-m1.face",
"displayName": "AISU SI-M1 Face SDK",
- "version": "0.1.0",
+ "version": "0.1.6",
"unity": "2021.3",
"description": "Unity UPM package for the AISU SI-M1 face recognition module protocol, transport, command services, lifecycle manager, tests, and demo sample.",
"author": {