蓝牙设置通过wifi同步

This commit is contained in:
“虞渠成” 2026-06-24 10:20:15 +08:00
parent d6eda72140
commit e69d3e4cbb
15 changed files with 186 additions and 15 deletions

View File

@ -9,7 +9,8 @@ extern "C" {
// 0 = Unknown, 1 = Resetting, 2 = Unsupported, 3 = Unauthorized, 4 = PoweredOff, 5 = PoweredOn
int GetBluetoothStateIOS() {
if (centralManager == nil) {
centralManager = [[CBCentralManager alloc] initWithDelegate:nil queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey: @NO}];
// 使用 @YES 允许 iOS 系统在蓝牙关闭时自动弹出"打开蓝牙"对话框
centralManager = [[CBCentralManager alloc] initWithDelegate:nil queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey: @YES}];
}
switch (centralManager.state) {

View File

@ -339,18 +339,18 @@ namespace Kill.Bluetooth
Log("iOS 蓝牙已开启");
onBluetoothReady?.Invoke();
}
// 4 = PoweredOff (蓝牙关闭)
// 4 = PoweredOff (蓝牙关闭) — iOS 系统会自动弹出蓝牙开启对话框,无需手动跳转设置
else if (state == 4)
{
Log("iOS 蓝牙未开启,跳转到系统设置...");
ShowIOSTurnOnBluetoothPrompt();
// 等待用户从设置返回(检测焦点变化 + 轮询蓝牙状态)
Log("iOS 蓝牙未开启,等待系统弹出蓝牙开启提示...");
// 不调用 ShowIOSTurnOnBluetoothPrompt(),让 iOS 原生弹窗处理
// 等待蓝牙状态变化(检测焦点变化 + 轮询蓝牙状态)
_enableBtCoroutine = StartCoroutine(WaitForIOSBluetoothEnabled(onBluetoothReady));
}
// 3 = Unauthorized (未授权)
// 3 = Unauthorized (未授权) — 需要跳转到应用设置页面让用户开启蓝牙权限
else if (state == 3)
{
Log("iOS 蓝牙未授权,跳转到系统设置...");
Log("iOS 蓝牙未授权,跳转到应用设置页...");
ShowIOSTurnOnBluetoothPrompt();
_enableBtCoroutine = StartCoroutine(WaitForIOSBluetoothEnabled(onBluetoothReady));
}

View File

@ -229,6 +229,73 @@ namespace Kill.Managers
#endregion
/// <summary>
/// 同步设备配置到服务器WiFi在线时调用由各设置页面Sync方法调用
/// </summary>
public async void SyncDeviceConfigToServer()
{
if (!hasWifi) return;
await UpdateDeviceConfig(selectedDevice.ble_mac, deviceConfig);
}
/// <summary>
/// 更新定时任务到服务器
/// </summary>
public async Task<bool> UpdateScheduleTasksToServer(string deviceSn, List<ScheduleTaskData> tasks)
{
var requestData = new ScheduleTasksUploadRequest
{
device_sn = deviceSn,
tasks = tasks
};
var response = await NetworkCtrl.Instance.Post<NoDataResponse>(
"/api/v1/device/schedule-tasks", JsonUtility.ToJson(requestData));
bool success = false;
ResponseCodeHandler.HandleResponse(response,
onSuccess: (data) =>
{
success = true;
Debug.Log($"[DataManager] 定时任务已同步到服务器,共 {tasks.Count} 条");
},
onError: (code, msg) =>
{
Debug.LogError($"[DataManager] 同步定时任务失败: {msg}");
}
);
return success;
}
/// <summary>
/// 更新指纹列表到服务器
/// </summary>
public async Task<bool> UpdateFingerprintsToServer(string deviceSn, List<FingerprintUserData> fingerprints)
{
var requestData = new FingerprintsUploadRequest
{
device_sn = deviceSn,
fingerprints = fingerprints
};
var response = await NetworkCtrl.Instance.Post<NoDataResponse>(
"/api/v1/device/fingerprints", JsonUtility.ToJson(requestData));
bool success = false;
ResponseCodeHandler.HandleResponse(response,
onSuccess: (data) =>
{
success = true;
Debug.Log($"[DataManager] 指纹列表已同步到服务器,共 {fingerprints.Count} 条");
},
onError: (code, msg) =>
{
Debug.LogError($"[DataManager] 同步指纹列表失败: {msg}");
}
);
return success;
}
/// <summary>
/// 通过WiFi获取指纹列表
/// </summary>
@ -352,4 +419,18 @@ namespace Kill.Managers.ResponseModels
public string mode;
public byte repeat_mask;
}
[System.Serializable]
public class ScheduleTasksUploadRequest
{
public string device_sn;
public List<ScheduleTaskData> tasks;
}
[System.Serializable]
public class FingerprintsUploadRequest
{
public string device_sn;
public List<FingerprintUserData> fingerprints;
}
}

View File

@ -173,6 +173,10 @@ public class ConnectDevicePageCtrl : MonoBehaviour
if (success)
{
UnityEngine.Debug.Log("WiFi 配置成功");
DataManager.Instance.deviceConfig.wifi_enable = true;
DataManager.Instance.deviceConfig.wifi_ssid = ssid;
DataManager.Instance.deviceConfig.wifi_password = password;
DataManager.Instance.SyncDeviceConfigToServer();
ShowSuccessPage();
}
else

View File

@ -128,14 +128,18 @@ namespace Kill.UI.Pages
LoadingUI.Show();
BLECommunicationManager.Instance.SetFingerprintEnable(nowUserid, isOn, (response) =>
{
Loom.QueueOnMainThread(() =>
Loom.QueueOnMainThread(async () =>
{
if (response.IsSuccess)
{
if (isOn)
SetFingerprint();
else
{
// 禁用指纹成功,同步到服务器
await SyncFingerprintsToServerAsync();
LoadingUI.Hide();
}
}
else
{
@ -150,11 +154,13 @@ namespace Kill.UI.Pages
{
BLECommunicationManager.Instance.StartFingerprintRecord(nowUserid, (response) =>
{
Loom.QueueOnMainThread(() =>
Loom.QueueOnMainThread(async () =>
{
if (response.IsSuccess)
{
fingerTip.SetActive(true);
// 录制成功,同步到服务器
await SyncFingerprintsToServerAsync();
LoadingUI.Hide();
}
else
@ -165,6 +171,14 @@ namespace Kill.UI.Pages
});
}
private async Task SyncFingerprintsToServerAsync()
{
if (!DataManager.Instance.hasWifi) return;
await DataManager.Instance.UpdateFingerprintsToServer(
DataManager.Instance.selectedDevice.device_sn,
DataManager.Instance.fingerprintUserDatas);
}
/// <summary>
/// 读取RGB设置
/// </summary>

View File

@ -171,6 +171,7 @@ namespace Kill.UI.Pages
if (success)
{
SyncSleepConfig();
DataManager.Instance.SyncDeviceConfigToServer();
UpdateUI();
}
else

View File

@ -80,6 +80,7 @@ namespace Kill.UI.Pages
if (success)
{
SyncRGBConfig();
DataManager.Instance.SyncDeviceConfigToServer();
UpdateUI();
}
else

View File

@ -124,6 +124,7 @@ namespace Kill.UI.Pages
if (success)
{
SyncMultimediaConfig();
DataManager.Instance.SyncDeviceConfigToServer();
UpdateUI();
}
else

View File

@ -372,6 +372,7 @@ namespace Kill.UI.Pages
Debug.Log($"[FovSettingPage] 角度设置成功: {selectedAngle}°");
currentAngle = selectedAngle;
DataManager.Instance.deviceConfig.fov_angle = selectedAngle * 10;
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -431,6 +432,7 @@ namespace Kill.UI.Pages
vllEnabled = newVllState;
UpdateVllButton();
DataManager.Instance.deviceConfig.laser_visible_enable = newVllState;
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -515,6 +517,7 @@ namespace Kill.UI.Pages
currentAngle = originalAngle;
selectedAngle = originalAngle;
DataManager.Instance.deviceConfig.fov_angle = originalAngle * 10;
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{

View File

@ -471,6 +471,7 @@ namespace Kill.UI.Pages
{
currentWorkMode = (WorkMode)mode;
deviceCtrl?.InitWrokMode(mode);
DataManager.Instance.deviceConfig.work_mode = DeviceConfig.ToServerString((WorkMode)mode);
}
return;
}
@ -511,6 +512,9 @@ namespace Kill.UI.Pages
{
deviceCtrl?.InitWrokMode(mode);
});
DataManager.Instance.deviceConfig.work_mode = DeviceConfig.ToServerString(setting.Mode);
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -1175,11 +1179,15 @@ namespace Kill.UI.Pages
DeviceLockControl deviceLockControl = new DeviceLockControl();
deviceLockControl.IsLocked = false;
OnLockReceived(deviceLockControl);
DataManager.Instance.deviceConfig.is_locked = false;
DataManager.Instance.SyncDeviceConfigToServer();
LoadingUI.Hide();
}
else
{
deviceCtrl.InitDeviceControl(false);
DataManager.Instance.deviceConfig.is_locked = true;
DataManager.Instance.SyncDeviceConfigToServer();
LoadingUI.Hide();
}
@ -1197,6 +1205,7 @@ namespace Kill.UI.Pages
if (success)
{
Debug.Log($"[HomePageCtrl] WiFi锁定设置成功: {(lockControl.IsLocked ? "" : "")}");
DataManager.Instance.deviceConfig.is_locked = lockControl.IsLocked;
if (lockControl.IsLocked)
{
deviceCtrl?.InitDeviceControl(false);
@ -1486,7 +1495,11 @@ namespace Kill.UI.Pages
{
Loom.QueueOnMainThread(() =>
{
if (success)
{
DataManager.Instance.deviceConfig.language = isChinese ? 1 : 0;
DataManager.Instance.SyncDeviceConfigToServer();
}
ReceiveStatus();
});

View File

@ -454,6 +454,7 @@ namespace Kill.UI.Pages
Debug.Log($"[LensSettingPage] 距离设置成功: {selectedLensValue}m");
currentLensValue = selectedLensValue;
DataManager.Instance.deviceConfig.detection_distance = (int)(selectedLensValue * 10);
DataManager.Instance.deviceConfig.aim_distance = (int)(selectedLensValue * 10);
}
else
{
@ -473,6 +474,8 @@ namespace Kill.UI.Pages
Debug.Log($"[LensSettingPage] 距离设置成功: {selectedLensValue}m");
currentLensValue = selectedLensValue;
DataManager.Instance.deviceConfig.detection_distance = (int)(selectedLensValue * 10);
DataManager.Instance.deviceConfig.aim_distance = (int)(selectedLensValue * 10);
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -531,6 +534,7 @@ namespace Kill.UI.Pages
vllEnabled = newVllState;
UpdateVllButton();
DataManager.Instance.deviceConfig.laser_visible_enable = newVllState;
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -650,6 +654,7 @@ namespace Kill.UI.Pages
currentLensValue = originalLensValue;
selectedLensValue = originalLensValue;
DataManager.Instance.deviceConfig.detection_distance = (ushort)Mathf.RoundToInt(originalLensValue * 10);
DataManager.Instance.deviceConfig.aim_distance = (ushort)Mathf.RoundToInt(originalLensValue * 10);
}
else
{
@ -673,6 +678,8 @@ namespace Kill.UI.Pages
currentLensValue = originalLensValue;
selectedLensValue = originalLensValue;
DataManager.Instance.deviceConfig.detection_distance = (ushort)Mathf.RoundToInt(originalLensValue * 10);
DataManager.Instance.deviceConfig.aim_distance = (ushort)Mathf.RoundToInt(originalLensValue * 10);
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{

View File

@ -396,6 +396,7 @@ namespace Kill.UI.Pages
{
Debug.Log($"[LightSettingPage] 灯光设置成功: 开关={selectedLightEnabled}, 类型={selectedLightType}, 强度={selectedIntensity}");
SyncLightConfigFromCurrent();
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{

View File

@ -438,6 +438,7 @@ namespace Kill.UI.Pages
{
Debug.Log($"[SafetySettingPage] 视觉检测设置成功: 开关={selectedVisualEnabled}, 灵敏度={selectedVisualSensitivity}");
SyncVisualConfig();
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -498,6 +499,7 @@ namespace Kill.UI.Pages
{
Debug.Log($"[SafetySettingPage] 毫米波雷达设置成功: 灵敏度={selectedWaveSensitivity}");
SyncRadarConfig();
DataManager.Instance.SyncDeviceConfigToServer();
}
else
{
@ -513,7 +515,9 @@ namespace Kill.UI.Pages
private void SyncRadarConfig()
{
var config = DataManager.Instance.deviceConfig;
config.radar_enable = true;
config.radar_sensitivity = DeviceConfig.ToServerString(selectedWaveSensitivity);
config.radar_safe_distance = 10;
}
/// <summary>

View File

@ -5,6 +5,7 @@ using Kill.Bluetooth;
using Kill.Bluetooth.Protocol;
using Kill.Core;
using Kill.Managers;
using Kill.Managers.ResponseModels;
using Kill.UI.Components;
using UnityEngine;
using UnityEngine.UI;
@ -607,7 +608,7 @@ namespace Kill.UI.Pages
BLECommunicationManager.Instance.WriteScheduleTasks(tasksToSave.ToArray(), (success) =>
{
Loom.QueueOnMainThread(() =>
Loom.QueueOnMainThread(async () =>
{
// 隐藏Loading
LoadingUI.Hide();
@ -615,6 +616,28 @@ namespace Kill.UI.Pages
if (success)
{
Debug.Log("[ScheduleSettingPage] 定时任务保存成功");
// WiFi在线时同步到服务器
if (DataManager.Instance.hasWifi)
{
var tasksData = new List<ScheduleTaskData>();
foreach (var t in tasksToSave)
{
tasksData.Add(new ScheduleTaskData
{
task_id = t.TaskId,
enabled = t.Enabled,
start_hour = t.StartHour,
start_minute = t.StartMinute,
end_hour = t.EndHour,
end_minute = t.EndMinute,
mode = ToModeString(t.Mode),
repeat_mask = t.Repeat
});
}
await DataManager.Instance.UpdateScheduleTasksToServer(
DataManager.Instance.selectedDevice.device_sn, tasksData);
}
}
else
{
@ -636,6 +659,19 @@ namespace Kill.UI.Pages
LoadingUI.Hide();
Destroy(gameObject);
}
private static string ToModeString(ScheduleTaskMode mode)
{
switch (mode)
{
case ScheduleTaskMode.Scan:
return "SCAN";
case ScheduleTaskMode.Sterilize:
return "STERILIZE";
default:
return "STANDBY";
}
}
}
}

View File

@ -18,9 +18,9 @@ PlayerSettings:
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.12156863, g: 0.12156863, b: 0.1254902, a: 1}
m_ShowUnitySplashScreen: 0
m_ShowUnitySplashLogo: 1
m_ShowUnitySplashLogo: 0
m_SplashScreenOverlayOpacity: 1
m_SplashScreenAnimation: 1
m_SplashScreenAnimation: 2
m_SplashScreenLogoStyle: 1
m_SplashScreenDrawMode: 0
m_SplashScreenBackgroundAnimationZoom: 1
@ -39,7 +39,11 @@ PlayerSettings:
y: 0
width: 1
height: 1
m_SplashScreenLogos: []
m_SplashScreenLogos:
- logo: {fileID: 21300000, guid: 0ddd579c89aad104fba3a316bd6c82fa, type: 3}
duration: 2
- logo: {fileID: 21300000, guid: ca5bf37fb627d9e4690c05d832243763, type: 3}
duration: 2
m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1920
@ -269,7 +273,7 @@ PlayerSettings:
AndroidTargetArchitectures: 2
AndroidTargetDevices: 1
AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0}
androidSplashScreen: {fileID: 2800000, guid: 0ddd579c89aad104fba3a316bd6c82fa, type: 3}
AndroidKeystoreName:
AndroidKeyaliasName:
AndroidEnableArmv9SecurityFeatures: 0