diff --git a/assets/html/h264.html b/assets/html/h264.html index 63303e3c..0995b612 100644 --- a/assets/html/h264.html +++ b/assets/html/h264.html @@ -1,86 +1,87 @@ + - + play + - - - + + // Function to return to Flutter page + function returnToFlutter() { + notifyFlutter("Returning to Flutter page"); + } + + // 添加清理方法 + function cleanupJMuxer() { + if (jmuxer) { + try { + jmuxer.destroy(); + jmuxer = null; + console.log('JMuxer cleaned up successfully'); + window.Flutter.postMessage('cleanup_complete'); + } catch (e) { + console.error('Error cleaning up JMuxer:', e); + window.Flutter.postMessage('cleanup_error'); + } + } + } + - + + \ No newline at end of file diff --git a/lan/lan_hk.json b/lan/lan_hk.json index 101c334c..49b5232c 100644 --- a/lan/lan_hk.json +++ b/lan/lan_hk.json @@ -173,9 +173,9 @@ "接收人信息": "收件人", "转移网关": "傳輸網關", "锁屏": "屏幕鎖定", - "已关闭": "關閉", - "已开启": "上", - "开启": "開", + "已关闭": "已關閉", + "已开启": "已開啟", + "开启": "開啟", "确定要开启重置键?": "繼續啟用Reset掣?", "确定要关闭重置键?": "繼續禁用Reset掣?", "隐藏无效开锁权限": "隱藏無效訪問權限", diff --git a/lan/lan_tw.json b/lan/lan_tw.json index f1ed8445..7110352a 100644 --- a/lan/lan_tw.json +++ b/lan/lan_tw.json @@ -172,9 +172,9 @@ "接收人信息": "收件人", "转移网关": "傳輸網關", "锁屏": "屏幕鎖定", - "已关闭": "關", - "已开启": "開啟", - "开启": "打開", + "已关闭": "已關閉", + "已开启": "已開啟", + "开启": "開啟", "确定要开启重置键?": "是否繼續啟用重置按鈕?", "确定要关闭重置键?": "是否繼續禁用重置按鈕?", "隐藏无效开锁权限": "隱藏無效訪問", diff --git a/lib/login/login/starLock_login_logic.dart b/lib/login/login/starLock_login_logic.dart index d7244068..f6130c6b 100755 --- a/lib/login/login/starLock_login_logic.dart +++ b/lib/login/login/starLock_login_logic.dart @@ -1,19 +1,21 @@ -import 'dart:io'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:star_lock/apm/apm_helper.dart'; -import 'package:jverify/jverify.dart'; import 'package:star_lock/appRouters.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/blue/blue_manage.dart'; +import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/mine/mine/starLockMine_state.dart'; import 'package:star_lock/mine/minePersonInfo/minePersonInfoPage/minePersonInfo_entity.dart'; import 'package:star_lock/network/start_chart_api.dart'; import 'package:star_lock/talk/starChart/entity/star_chart_register_node_entity.dart'; +import 'package:star_lock/tools/appFirstEnterHandle.dart'; import 'package:star_lock/tools/baseGetXController.dart'; +import 'package:star_lock/translations/current_locale_tool.dart'; import '../../main/lockMian/lockMain/lockMain_logic.dart'; import '../../mine/mine/starLockMine_logic.dart'; @@ -143,7 +145,7 @@ class StarLockLoginLogic extends BaseGetXController { if (entity.errorCode!.codeIsSuccessful) { if (state.countryName != entity.data!.name) { ShowTipView().showSureAlertDialog( - '国家地区的选择将影响数据安全,你当前选择的是'+state.countryName+'请确认后再继续'.tr, + '国家地区的选择将影响数据安全,你当前选择的是' + state.countryName + '请确认后再继续'.tr, tipTitle: '确认国家或地区'.tr, sureStr: '我知道了'.tr); } @@ -175,20 +177,43 @@ class StarLockLoginLogic extends BaseGetXController { state.canNext.value = state.pwdIsOK && state.isEmailOrPhone; } + late StreamSubscription _agreePrivacySubscription; + + void _initEventListen() { + _agreePrivacySubscription = eventBus + .on() + .listen((AgreePrivacyAgreement event) async { + /// 检查ip如果属于国内才进行初始化 + final CheckIPEntity entity = await ApiRepository.to.checkIpAction(ip: ''); + String currentLanguage = + CurrentLocaleTool.getCurrentLocaleString(); // 当前选择语言 + // 判断如果ip是国内的且选的是中文才初始化一键登录 + if (entity.data!.abbreviation?.toLowerCase() == 'cn' && + currentLanguage == 'zh_CN') { + // 初始化一键登录服务 + await JverifyOneClickLoginManage(); + state.isCheckVerifyEnable.value = + await JverifyOneClickLoginManage().checkVerifyEnable(); + AppLog.log('一键登录初始化认证结果:${state.isCheckVerifyEnable.value}'); + } + }); + } + @override Future onInit() async { + _initEventListen(); super.onInit(); - - } @override void onClose() { + // 取消事件监听 + _agreePrivacySubscription.cancel(); state.onClose(); super.onClose(); } - //刷新设备信息 +//刷新设备信息 Future flushedDeviceInfo() async { XSConstantMacro().getDeviceInfoData().then((Map data) { state.deviceInfoMap.value = data; diff --git a/lib/main.dart b/lib/main.dart index 9e20bdd8..69901fff 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,19 +10,23 @@ import 'package:path/path.dart' as path; import 'package:provider/provider.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/login/login/starLock_login_logic.dart'; +import 'package:star_lock/login/register/entity/checkIP_entity.dart'; import 'package:star_lock/mine/about/debug/debug_tool.dart'; import 'package:star_lock/network/api_provider.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/network/start_chart_api.dart'; import 'package:star_lock/talk/starChart/handle/impl/debug_Info_model.dart'; import 'package:star_lock/talk/starChart/status/appLifecycle_observer.dart'; +import 'package:star_lock/tools/baseGetXController.dart'; import 'package:star_lock/tools/bugly/bugly_tool.dart'; import 'package:star_lock/tools/device_info_service.dart'; +import 'package:star_lock/tools/eventBusEventManage.dart'; import 'package:star_lock/tools/jverify_one_click_login.dart'; import 'package:star_lock/tools/platform_info_services.dart'; import 'package:star_lock/tools/push/notification_service.dart'; import 'package:star_lock/tools/push/xs_jPhush.dart'; import 'package:star_lock/tools/storage.dart'; +import 'package:star_lock/translations/current_locale_tool.dart'; import 'package:star_lock/translations/trans_lib.dart'; import 'apm/apm_helper.dart'; @@ -102,10 +106,20 @@ Future privacySDKInitialization() async { await jpushProvider.initJPushService(); NotificationService().init(); // 初始化通知服务 - // 初始化一键登录服务 - final StarLockLoginLogic loginLogic = Get.put(StarLockLoginLogic()); - JverifyOneClickLoginManage(); - loginLogic.oneClickLoginAction(); - loginLogic.state.isCheckVerifyEnable.value = - await JverifyOneClickLoginManage().checkVerifyEnable(); + /// 检查ip如果属于国内才进行初始化 + final CheckIPEntity entity = await ApiRepository.to.checkIpAction(ip: ''); + if (entity.errorCode!.codeIsSuccessful) { + String currentLanguage = + CurrentLocaleTool.getCurrentLocaleString(); // 当前选择语言 + // 判断如果ip是国内的且选的是中文才初始化一键登录 + if (entity.data!.abbreviation?.toLowerCase() == 'cn' && + currentLanguage == 'zh_CN') { + // 初始化一键登录服务 + final StarLockLoginLogic loginLogic = Get.put(StarLockLoginLogic()); + await JverifyOneClickLoginManage(); + loginLogic.state.isCheckVerifyEnable.value = + await JverifyOneClickLoginManage().checkVerifyEnable(); + eventBus.fire(AgreePrivacyAgreement()); + } + } } diff --git a/lib/main/lockDetail/card/addICCard/addICCard_logic.dart b/lib/main/lockDetail/card/addICCard/addICCard_logic.dart index dda931b0..2729347b 100755 --- a/lib/main/lockDetail/card/addICCard/addICCard_logic.dart +++ b/lib/main/lockDetail/card/addICCard/addICCard_logic.dart @@ -80,7 +80,7 @@ class AddICCardLogic extends BaseGetXController { final List token = reply.data.sublist(5, 9); final List saveStrList = changeIntListToStringList(token); Storage.setStringList(saveBlueToken, saveStrList); - // AppLog.log('添加卡token:$token'); + AppLog.log('添加卡token:$token'); IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( keyID: '1', @@ -107,6 +107,14 @@ class AddICCardLogic extends BaseGetXController { token: token, isBeforeAddUser: false); break; + case 0xFE: + case 12: + // 管理员已满 + state.ifAddState.value = false; + showToast('管理员已满'.tr, something: () { + Get.back(); + }); + break; default: //失败 state.ifAddState.value = false; @@ -146,6 +154,7 @@ class AddICCardLogic extends BaseGetXController { Get.close(1); break; case 0xFE: + case 0x12: // 管理员已满 showToast('管理员已满'.tr); Get.close(1); diff --git a/lib/main/lockDetail/card/cardList/cardList_logic.dart b/lib/main/lockDetail/card/cardList/cardList_logic.dart index a11cd18c..022ff0fb 100755 --- a/lib/main/lockDetail/card/cardList/cardList_logic.dart +++ b/lib/main/lockDetail/card/cardList/cardList_logic.dart @@ -28,6 +28,7 @@ class CardListLogic extends BaseGetXController { // 获取解析后的数据 late StreamSubscription _replySubscription; + void _initReplySubscription() { _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) { @@ -69,11 +70,15 @@ class CardListLogic extends BaseGetXController { userID: (await Storage.getUid())!, cardNo: state.deletCardNo, useCountLimit: 0xffff, - operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + operate: state.isDeletAll == true ? 3 : 2, + // 0:注册 1:修改 2:删除 3:删除全部 isAdmin: 0, - isForce: 0, // 是否是胁迫 - isRound: 0, // 是否是循环 - weekRound: 0, // 周循环 + isForce: 0, + // 是否是胁迫 + isRound: 0, + // 是否是循环 + weekRound: 0, + // 周循环 startDate: 0x11223344, endDate: 0x11223344, startTime: '0', @@ -116,11 +121,15 @@ class CardListLogic extends BaseGetXController { userID: (await Storage.getUid())!, cardNo: state.deletCardNo, useCountLimit: 0xffff, - operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + operate: state.isDeletAll == true ? 3 : 2, + // 0:注册 1:修改 2:删除 3:删除全部 isAdmin: 0, - isForce: 0, // 是否是胁迫 - isRound: 0, // 是否是循环 - weekRound: 0, // 周循环 + isForce: 0, + // 是否是胁迫 + isRound: 0, + // 是否是循环 + weekRound: 0, + // 周循环 startDate: 0x11223344, endDate: 0x11223344, startTime: '0', @@ -193,6 +202,7 @@ class CardListLogic extends BaseGetXController { // 监听修改完详情之后刷新列表 late StreamSubscription _teamEvent; + void _initRefreshAction() { _teamEvent = eventBus .on() @@ -240,6 +250,7 @@ class CardListLogic extends BaseGetXController { _initRefreshAction(); } + await getICCardListData(isRefresh: true); } @override diff --git a/lib/main/lockDetail/card/cardList/cardList_page.dart b/lib/main/lockDetail/card/cardList/cardList_page.dart index 705d5c8c..f5fe77e1 100755 --- a/lib/main/lockDetail/card/cardList/cardList_page.dart +++ b/lib/main/lockDetail/card/cardList/cardList_page.dart @@ -31,24 +31,24 @@ class _CardListPageState extends State with RouteAware { final CardListLogic logic = Get.put(CardListLogic()); final CardListState state = Get.find().state; - Future getHttpData({required bool isRefresh}) async { - final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); - if (isDemoMode == false) { - logic - .getICCardListData(isRefresh: isRefresh) - .then((FingerprintListDataEntity value) { - if (mounted) { - setState(() {}); - } - }); - } - } + // Future logic.getICCardListData({required bool isRefresh}) async { + // final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + // if (isDemoMode == false) { + // logic + // .getICCardListData(isRefresh: isRefresh) + // .then((FingerprintListDataEntity value) { + // if (mounted) { + // setState(() {}); + // } + // }); + // } + // } @override void initState() { super.initState(); - getHttpData(isRefresh: true); + // logic.getICCardListData(isRefresh: true); } @override @@ -92,17 +92,17 @@ class _CardListPageState extends State with RouteAware { ), body: EasyRefreshTool( onRefresh: () { - getHttpData(isRefresh: true); + logic.getICCardListData(isRefresh: true); }, onLoad: () { - getHttpData(isRefresh: false); + logic.getICCardListData(isRefresh: false); }, child: Column( children: [ KeySearchWidget( editingController: state.searchController, onSubmittedAction: () { - getHttpData(isRefresh: true); + logic.getICCardListData(isRefresh: true); }, ), SizedBox(height: 20.h), @@ -115,10 +115,10 @@ class _CardListPageState extends State with RouteAware { 'fromType': 1 // 1从添加钥匙列表进入 2从考勤添加员工入口进入 })! .then((value) { - getHttpData(isRefresh: true); + logic.getICCardListData(isRefresh: true); }); // if (data != null) { - // getHttpData(isRefresh: true); + // logic.getICCardListData(isRefresh: true); // } }, ), @@ -180,9 +180,9 @@ class _CardListPageState extends State with RouteAware { arguments: { 'fingerprintItemData': fingerprintItemData, })! - .then((value) => getHttpData(isRefresh: true)); + .then((value) => logic.getICCardListData(isRefresh: true)); // if (data != null) { - // getHttpData(isRefresh: true); + // logic.getICCardListData(isRefresh: true); // } }), ); diff --git a/lib/main/lockDetail/face/addFace/addFace_logic.dart b/lib/main/lockDetail/face/addFace/addFace_logic.dart index d7fe6bd3..5900580c 100755 --- a/lib/main/lockDetail/face/addFace/addFace_logic.dart +++ b/lib/main/lockDetail/face/addFace/addFace_logic.dart @@ -54,7 +54,7 @@ class AddFaceLogic extends BaseGetXController { // 最大图片数 state.maxRegCount.value = reply.data[11]; - // AppLog.log('人脸开始state.maxRegCount.value:${state.maxRegCount.value}'); + AppLog.log('人脸开始state.maxRegCount.value:${state.maxRegCount.value}'); break; case 0x06: //无权限 @@ -89,6 +89,12 @@ class AddFaceLogic extends BaseGetXController { isBeforeAddUser: false ); break; + case 0xFE: + case 12: + // 管理员已满 + showToast('管理员已满'.tr); + Get.close(1); + break; default: //失败 state.ifAddState.value = false; @@ -112,9 +118,9 @@ class AddFaceLogic extends BaseGetXController { Get.close(1); break; case 0xFE: + case 12: // 管理员已满 showToast('管理员已满'.tr); - state.ifAddState.value = false; Get.close(1); break; case 0xFD: @@ -138,7 +144,7 @@ class AddFaceLogic extends BaseGetXController { // 添加人脸中 // 当前注册数 state.regIndex.value = reply.data[6]; - // AppLog.log('注册人脸过程state.regIndex.value:${state.regIndex.value}'); + AppLog.log('注册人脸过程state.regIndex.value:${state.regIndex.value}'); break; } diff --git a/lib/main/lockDetail/face/faceList/faceList_logic.dart b/lib/main/lockDetail/face/faceList/faceList_logic.dart index debdc5ce..9781a916 100755 --- a/lib/main/lockDetail/face/faceList/faceList_logic.dart +++ b/lib/main/lockDetail/face/faceList/faceList_logic.dart @@ -22,6 +22,7 @@ class FaceListLogic extends BaseGetXController { // 获取解析后的数据 late StreamSubscription _replySubscription; + void _initReplySubscription() { _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) { @@ -77,11 +78,15 @@ class FaceListLogic extends BaseGetXController { userID: (await Storage.getUid())!, faceNo: state.deletFaceNo, useCountLimit: 0xffff, - operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + operate: state.isDeletAll == true ? 3 : 2, + // 0:注册 1:修改 2:删除 3:删除全部 isAdmin: 0, - isForce: 0, // 是否是胁迫 - isRound: 0, // 是否是循环 - weekRound: 0, // 周循环 + isForce: 0, + // 是否是胁迫 + isRound: 0, + // 是否是循环 + weekRound: 0, + // 周循环 startDate: 0x11223344, endDate: 0x11223344, startTime: '0', @@ -277,11 +282,15 @@ class FaceListLogic extends BaseGetXController { userID: (await Storage.getUid())!, faceNo: state.deletFaceNo, useCountLimit: 0xffff, - operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + operate: state.isDeletAll == true ? 3 : 2, + // 0:注册 1:修改 2:删除 3:删除全部 isAdmin: 0, - isForce: 0, // 是否是胁迫 - isRound: 0, // 是否是循环 - weekRound: 0, // 周循环 + isForce: 0, + // 是否是胁迫 + isRound: 0, + // 是否是循环 + weekRound: 0, + // 周循环 startDate: 0x11223344, endDate: 0x11223344, startTime: '0', @@ -348,8 +357,8 @@ class FaceListLogic extends BaseGetXController { lockId: state.lockId.value, ); if (entity.errorCode!.codeIsSuccessful) { - showToast('删除成功'.tr, something: () { - getFaceListData(isRefresh: true); + showToast('删除成功'.tr, something: () async { + await getFaceListData(isRefresh: true); }); } } @@ -366,8 +375,8 @@ class FaceListLogic extends BaseGetXController { lockId: state.lockId.value, ); if (entity.errorCode!.codeIsSuccessful) { - showToast('重置成功'.tr, something: () { - getFaceListData(isRefresh: true); + showToast('重置成功'.tr, something: () async{ + await getFaceListData(isRefresh: true); }); } } @@ -403,11 +412,12 @@ class FaceListLogic extends BaseGetXController { // 监听修改完详情之后刷新列表 late StreamSubscription _teamEvent; + void _initRefreshAction() { _teamEvent = eventBus .on() - .listen((OtherTypeRefreshListEvent event) { - getFaceListData(isRefresh: true); + .listen((OtherTypeRefreshListEvent event) async { + await getFaceListData(isRefresh: true); }); } @@ -434,6 +444,7 @@ class FaceListLogic extends BaseGetXController { // senderCheckingUserInfoCount(); } + getFaceListData(isRefresh: true); } @override diff --git a/lib/main/lockDetail/face/faceList/faceList_page.dart b/lib/main/lockDetail/face/faceList/faceList_page.dart index 5758785b..43248d26 100755 --- a/lib/main/lockDetail/face/faceList/faceList_page.dart +++ b/lib/main/lockDetail/face/faceList/faceList_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -31,24 +30,6 @@ class _FaceListPageState extends State with RouteAware { final FaceListLogic logic = Get.put(FaceListLogic()); final FaceListState state = Get.find().state; - Future getHttpData({required bool isRefresh}) async { - final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); - if (isDemoMode == false) { - logic.getFaceListData(isRefresh: isRefresh).then((FingerprintListDataEntity value) { - if (mounted) { - setState(() {}); - } - }); - } - } - - @override - void initState() { - super.initState(); - - getHttpData(isRefresh: true); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -92,17 +73,17 @@ class _FaceListPageState extends State with RouteAware { ), body: EasyRefreshTool( onRefresh: () { - getHttpData(isRefresh: true); + logic.getFaceListData(isRefresh: true); }, onLoad: () { - getHttpData(isRefresh: false); + logic.getFaceListData(isRefresh: false); }, child: Column( children: [ KeySearchWidget( editingController: state.searchController, onSubmittedAction: () { - getHttpData(isRefresh: true); + logic.getFaceListData(isRefresh: true); }, ), SizedBox( @@ -110,14 +91,15 @@ class _FaceListPageState extends State with RouteAware { ), Expanded(child: _buildMainUI()), AddBottomWhiteBtn( - btnName: - '添加人脸'.tr, + btnName: '添加人脸'.tr, onClick: () async { - await Get.toNamed(Routers.addFaceTypePage, arguments: { + await Get.toNamed(Routers.addFaceTypePage, + arguments: { 'lockId': state.lockId.value, 'fromType': 1 // 1从添加钥匙列表进入 2从考勤添加员工入口进入 - })!.then((value) { - getHttpData(isRefresh: true); + })! + .then((value) { + logic.getFaceListData(isRefresh: true); }); }, ), @@ -154,9 +136,11 @@ class _FaceListPageState extends State with RouteAware { logic.getKeyType(getFaceItemData), logic.getKeyDateType(getFaceItemData), () async { await Get.toNamed(Routers.faceDetailPage, - arguments: { + arguments: { 'faceItemData': getFaceItemData, - })!.then((value) => getHttpData(isRefresh: true)); + })! + .then((value) => + logic.getFaceListData(isRefresh: true)); }), ); } diff --git a/lib/main/lockDetail/fingerprint/addFingerprint/addFingerprint_logic.dart b/lib/main/lockDetail/fingerprint/addFingerprint/addFingerprint_logic.dart index c8507a3f..c5ff8d1c 100755 --- a/lib/main/lockDetail/fingerprint/addFingerprint/addFingerprint_logic.dart +++ b/lib/main/lockDetail/fingerprint/addFingerprint/addFingerprint_logic.dart @@ -105,6 +105,14 @@ class AddFingerprintLogic extends BaseGetXController { token: token, isBeforeAddUser: false); break; + case 0xFE: + case 12: + // 管理员已满 + state.ifAddState.value = false; + showToast('管理员已满'.tr, something: () { + Get.back(); + }); + break; default: //失败 state.ifAddState.value = false; @@ -132,10 +140,12 @@ class AddFingerprintLogic extends BaseGetXController { Get.close(1); break; case 0xFE: - // 管理员已满 - showToast('管理员已满'.tr); + case 12: + // 管理员已满 state.ifAddState.value = false; - Get.close(1); + showToast('管理员已满'.tr, something: () { + Get.back(); + }); break; case 0xFD: // 用户已满 diff --git a/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintList_logic.dart b/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintList_logic.dart index 09523db0..3c886df8 100755 --- a/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintList_logic.dart +++ b/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintList_logic.dart @@ -404,8 +404,8 @@ class FingerprintListLogic extends BaseGetXController { void _initRefreshAction() { _teamEvent = eventBus .on() - .listen((OtherTypeRefreshListEvent event) { - getFingerprintsListData(isRefresh: true); + .listen((OtherTypeRefreshListEvent event) async { + await getFingerprintsListData(isRefresh: true); }); } @@ -460,7 +460,7 @@ class FingerprintListLogic extends BaseGetXController { if (isDemoMode == false) { _initReplySubscription(); - _initRefreshAction(); + // _initRefreshAction(); getFingerprintsListData(isRefresh: true); } } diff --git a/lib/main/lockDetail/lockDetail/lockDetail_logic.dart b/lib/main/lockDetail/lockDetail/lockDetail_logic.dart index ee7ba5f8..f3668a2c 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_logic.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_logic.dart @@ -7,6 +7,7 @@ import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/apm/apm_helper.dart'; +import 'package:star_lock/appRouters.dart'; import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyListEntity.dart'; @@ -285,7 +286,7 @@ class LockDetailLogic extends BaseGetXController { final List publicKeyData = state.keyInfos.value.bluetooth!.publicKey!.cast(); final List saveStrList = changeIntListToStringList(publicKeyData); - await Storage.setStringList(saveBluePublicKey, saveStrList); + await Storage.setStringList(saveBluePublicKey, saveStrList); // 私钥 final List privateKeyData = @@ -298,7 +299,7 @@ class LockDetailLogic extends BaseGetXController { final List signKeyData = state.keyInfos.value.bluetooth!.signKey!.cast(); final List saveSignKeyList = changeIntListToStringList(signKeyData); - await Storage.setStringList(saveBlueSignKey, saveSignKeyList); + await Storage.setStringList(saveBlueSignKey, saveSignKeyList); final bool ifHaveKey = await Storage.ifHaveKey(saveBlueToken); if (!ifHaveKey) { @@ -814,7 +815,7 @@ class LockDetailLogic extends BaseGetXController { } }); - eventBus + state.DetailLockInfo = eventBus .on() .listen((PassCurrentLockInformationEvent event) { if (event.lockSetInfoData.lockSettingInfo != null && @@ -838,5 +839,12 @@ class LockDetailLogic extends BaseGetXController { state.keyInfos.refresh(); } }); + + state.SuccessfulDistributionNetworkEvent = eventBus + .on() + .listen((SuccessfulDistributionNetwork event) { + // 配网成功获取一下配网信息 + requestDeviceNetworkInfo(); + }); } } diff --git a/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/lib/main/lockDetail/lockDetail/lockDetail_page.dart index 4352d93d..1040b5ba 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_page.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_page.dart @@ -89,7 +89,6 @@ class _LockDetailPageState extends State AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(context)!); state.isOpenLockNeedOnline.refresh(); - logic.requestDeviceNetworkInfo(); } StreamSubscription? _lockRefreshLockDetailInfoDataEvent; @@ -1024,7 +1023,7 @@ class _LockDetailPageState extends State Widget widget = getBottomWidget()[index]; return widget; }, - physics: const NeverScrollableScrollPhysics(), + // physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, ) diff --git a/lib/main/lockDetail/lockDetail/lockDetail_state.dart b/lib/main/lockDetail/lockDetail/lockDetail_state.dart index 760f1289..01178f52 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_state.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_state.dart @@ -14,6 +14,8 @@ class LockDetailState { late StreamSubscription replySubscription; StreamSubscription? lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent; StreamSubscription? LockSetChangeSetRefreshLockDetailWithTypeSubscription; + StreamSubscription? DetailLockInfo; + StreamSubscription? SuccessfulDistributionNetworkEvent; String lockNetToken = '0'; int differentialTime = 0;// 服务器时间与本地时间差值 diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart index f4688019..25c8e43d 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart @@ -62,18 +62,15 @@ class ConfiguringWifiLogic extends BaseGetXController { peerId: peerId, ); if (entity.errorCode!.codeIsSuccessful) { + // 设置锁的peerID + StartChartManage().lockNetworkInfo = DeviceNetworkInfo( + wifiName: wifiName, + networkMac: networkMac, + secretKey: secretKey, + peerId: peerId, + ); + await _getUploadLockSet(); - showToast('配网成功'.tr, something: () async { - eventBus - .fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); - // 设置锁的peerID - StartChartManage().lockNetworkInfo = DeviceNetworkInfo( - wifiName: wifiName, - networkMac: networkMac, - secretKey: secretKey, - peerId: peerId, - ); - }); } } @@ -107,8 +104,11 @@ class ConfiguringWifiLogic extends BaseGetXController { // WIFI配网结果 Future _replySenderConfiguringWifiResult(Reply reply) async { final int status = reply.data[2]; - state.sureBtnState.value = 0; - state.isLoading.value = false; + // state.sureBtnState.value = 0; + + // 取消loading超时定时器 + state.loadingTimer?.cancel(); + state.loadingTimer = null; switch (status) { case 0x00: @@ -135,21 +135,23 @@ class ConfiguringWifiLogic extends BaseGetXController { // 保存到缓存 await Storage.saveLockNetWorkInfo(jsonMap); // 上报服务器 - updateNetworkInfo( + updateNetworkInfo( peerId: peerId ?? '', wifiName: wifiName ?? '', secretKey: secretKey ?? '', deviceMac: deviceMac ?? '', networkMac: networkMac ?? ''); + break; default: //失败 - dismissEasyLoading(); + dismissEasyLoading(); // 关闭loading + cancelBlueConnetctToastTimer(); if (state.loadingTimer != null) { state.loadingTimer!.cancel(); state.loadingTimer = null; } - cancelBlueConnetctToastTimer(); + showToast('配网失败'.tr); state.isLoading.value = false; break; @@ -165,7 +167,7 @@ class ConfiguringWifiLogic extends BaseGetXController { // 点击配置wifi Future senderConfiguringWifiAction() async { AppLog.log('开始配网${EasyLoading.isShow}'); - EasyLoading.show(); + if (state.isLoading.isTrue) { AppLog.log('正在配网中请勿重复点击'); return; @@ -179,10 +181,10 @@ class ConfiguringWifiLogic extends BaseGetXController { showToast('请输入WiFi密码'.tr); return; } - if (state.sureBtnState.value == 1) { - return; - } - state.sureBtnState.value = 1; + // if (state.sureBtnState.value == 1) { + // return; + // } + // state.sureBtnState.value = 1; final GetGatewayConfigurationEntity entity = await ApiRepository.to.getGatewayConfigurationNotLoading(timeout: 60); @@ -219,9 +221,13 @@ class ConfiguringWifiLogic extends BaseGetXController { // 如果为空,则直接赋值 state.getGatewayConfigurationStr = "{\"userPeerld\": \"$appPeerId\"}"; } + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + state.isLoading.value = false; + }); - AppLog.log('获取到配网信息===开始发送蓝牙指令${EasyLoading.isShow}'); - + // 发送配网指令 BlueManage().blueSendData( BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async { @@ -231,31 +237,19 @@ class ConfiguringWifiLogic extends BaseGetXController { password: state.wifiPWDController.text, gatewayConfigurationStr: state.getGatewayConfigurationStr, ); + } else if (connectionState == BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + state.isLoading.value = false; + // state.sureBtnState.value = 0; + if (state.ifCurrentScreen.value == true) { + showBlueConnetctToast(); + } } }, - isAddEquipment: true, + isAddEquipment: false, ); - state.loadingTimer ??= Timer.periodic(Duration(milliseconds: 100), (timer) { - if (!EasyLoading.isShow) { - EasyLoading.show(); - } - }); - state.isLoading.value = true; - // 添加15秒超时检查 - Future.delayed(const Duration(seconds: 15), () { - if (state.isLoading.isTrue) { - EasyLoading.dismiss(); - state.isLoading.value = false; - state.sureBtnState.value = 0; - if (state.loadingTimer != null) { - state.loadingTimer!.cancel(); - state.loadingTimer = null; - } - showToast('配网失败'.tr); - } - }); - AppLog.log('发送方法执行完毕${EasyLoading.isShow}'); } // 获取设备状态 @@ -314,7 +308,7 @@ class ConfiguringWifiLogic extends BaseGetXController { getWifiLockServiceIpAndPort(); _initReplySubscription(); - getDevicesStatusAction(); + // getDevicesStatusAction(); } @override @@ -330,13 +324,13 @@ class ConfiguringWifiLogic extends BaseGetXController { void _replyGatewayGetStatusReply(GatewayGetStatusReply reply) { final int status = reply.data[2]; - + //成功 + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); switch (status) { case 0x00: //成功 - // state.sureBtnState.value = 0; - cancelBlueConnetctToastTimer(); - dismissEasyLoading(); + // state.sureBtnState.value = 0; final GetGatewayInfoModel gatewayModel = GetGatewayInfoModel(); // 网关MAC地址 @@ -387,28 +381,17 @@ class ConfiguringWifiLogic extends BaseGetXController { } } - void _replyStatusInfo(reply) {} - // 上传数据获取设置 Future _getUploadLockSet() async { showEasyLoading(); showBlueConnetctToastTimer(action: () { dismissEasyLoading(); }); - BlueManage().blueSendData(BlueManage().connectDeviceName, - (BluetoothConnectionState connectionState) async { - if (connectionState == BluetoothConnectionState.connected) { - final List? token = await Storage.getStringList(saveBlueToken); - final List getTokenList = changeStringListToIntList(token!); - _uploadLockSet(getTokenList); - } else if (connectionState == BluetoothConnectionState.disconnected) { - cancelBlueConnetctToastTimer(); - if (state.ifCurrentScreen.value == true) { - showBlueConnetctToast(); - } - } - }); + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + await _uploadLockSet(getTokenList); } // 公共的上传锁设置 @@ -432,15 +415,15 @@ class ConfiguringWifiLogic extends BaseGetXController { // 上传数据获取锁设置解析 Future _replyUpdataLockSetReply(Reply reply) async { final int status = reply.data[2]; + dismissEasyLoading(); // 关闭loading + cancelBlueConnetctToastTimer(); switch (status) { case 0x00: - //成功 - dismissEasyLoading(); - cancelBlueConnetctToastTimer(); - _lockDataUpload( + await _lockDataUpload( uploadType: 1, recordType: 0, records: reply.data.sublist(7, reply.data.length)); + break; case 0x06: //无权限 @@ -469,30 +452,18 @@ class ConfiguringWifiLogic extends BaseGetXController { records: records, isUnShowLoading: true); if (entity.errorCode!.codeIsSuccessful) { - await Future.delayed((Duration(seconds: 1))); - if (state.pageName.value == 'lockSet') { - Get.close(2); - } else { - Get.offAllNamed(Routers.starLockMain); - } - dismissEasyLoading(); - if (state.loadingTimer != null) { - state.loadingTimer!.cancel(); - state.loadingTimer = null; - } - } - } + showToast('配网成功'.tr, something: () { + state.isLoading.value = false; + if (state.pageName.value == 'lockSet') { + Get.close(2); + } else { + Get.offAllNamed(Routers.starLockMain); + } - @override - void dispose() { - dismissEasyLoading(); - // TODO: implement dispose - super.dispose(); - state.isLoading.value = false; - if (state.loadingTimer != null) { - state.loadingTimer!.cancel(); - state.loadingTimer = null; + eventBus + .fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); + eventBus.fire(SuccessfulDistributionNetwork()); + }); } - } } diff --git a/lib/main/lockDetail/lockSet/normallyOpenMode/normallyOpenMode_logic.dart b/lib/main/lockDetail/lockSet/normallyOpenMode/normallyOpenMode_logic.dart index c748da0c..c63750d7 100755 --- a/lib/main/lockDetail/lockSet/normallyOpenMode/normallyOpenMode_logic.dart +++ b/lib/main/lockDetail/lockSet/normallyOpenMode/normallyOpenMode_logic.dart @@ -1,4 +1,3 @@ - import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; @@ -17,51 +16,59 @@ import '../../../../tools/eventBusEventManage.dart'; import '../../../../tools/storage.dart'; import 'normallyOpenMode_state.dart'; -class NormallyOpenModeLogic extends BaseGetXController{ +class NormallyOpenModeLogic extends BaseGetXController { NormallyOpenModeState state = NormallyOpenModeState(); // 配置锁的常开模式设置 - Future configPassageMode() async{ - if(state.weekDays.value.isEmpty){ + Future configPassageMode() async { + if (state.weekDays.value.isEmpty) { showToast('请选择常开日期'.tr); return; } - if(state.endTimeMinute.value < state.beginTimeMinute.value){ + if (state.endTimeMinute.value < state.beginTimeMinute.value) { showToast('结束时间不能小于开始时间哦'.tr); return; } final List passageModeConfig = []; final Map map = { - 'isAllDay':state.isAllDay.value, - 'weekDays':state.weekDays.value, - 'startDate':state.beginTimeMinute.value, - 'endDate':state.endTimeMinute.value, + 'isAllDay': state.isAllDay.value, + 'weekDays': state.weekDays.value, + 'startDate': state.beginTimeMinute.value, + 'endDate': state.endTimeMinute.value, }; passageModeConfig.add(map); final LoginEntity entity = await ApiRepository.to.setNormallyModeData( lockId: state.lockSetInfoData.value.lockId!, - passageMode:state.isOpenNormallyOpenMode.value == true ? 1:0, + passageMode: state.isOpenNormallyOpenMode.value == true ? 1 : 0, passageModeConfig: passageModeConfig, ); - if(entity.errorCode!.codeIsSuccessful){ - showToast('操作成功'.tr, something: (){ + if (entity.errorCode!.codeIsSuccessful) { + showToast('操作成功'.tr, something: () { eventBus.fire(RefreshLockListInfoDataEvent()); - state.lockSetInfoData.value.lockSettingInfo!.passageMode = state.isOpenNormallyOpenMode.value == true ? 1:0; - eventBus.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); - eventBus.fire(LockSetChangeSetRefreshLockDetailWithType(2, state.lockSetInfoData.value.lockSettingInfo!.passageMode!.toString())); + state.lockSetInfoData.value.lockSettingInfo!.passageMode = + state.isOpenNormallyOpenMode.value == true ? 1 : 0; + eventBus + .fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value)); + eventBus.fire(LockSetChangeSetRefreshLockDetailWithType( + 2, + state.lockSetInfoData.value.lockSettingInfo!.passageMode! + .toString())); + Get.back(); }); } } // 获取解析后的数据 late StreamSubscription _replySubscription; + void _initReplySubscription() { - _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) { - if(reply is SetSupportFunctionsWithParametersReply) { + _replySubscription = + EventBusManager().eventBus!.on().listen((Reply reply) { + if (reply is SetSupportFunctionsWithParametersReply) { _replySetSupportFunctionsWithParameters(reply); } @@ -93,7 +100,7 @@ class NormallyOpenModeLogic extends BaseGetXController{ // 设置自动落锁数据解析 Future _replySetSupportFunctionsWithParameters(Reply reply) async { final int status = reply.data[2]; - switch(status){ + switch (status) { case 0x00: //成功 state.sureBtnState.value = 0; @@ -111,38 +118,44 @@ class NormallyOpenModeLogic extends BaseGetXController{ // 设置支持功能(带参数) Future sendAutoLock() async { - if(state.sureBtnState.value == 1){ + if (state.sureBtnState.value == 1) { return; } state.sureBtnState.value = 1; showEasyLoading(); - showBlueConnetctToastTimer(action: (){ + showBlueConnetctToastTimer(action: () { dismissEasyLoading(); state.sureBtnState.value = 0; }); - BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async { + BlueManage().blueSendData(BlueManage().connectDeviceName, + (BluetoothConnectionState connectionState) async { if (connectionState == BluetoothConnectionState.connected) { - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); - final List? publicKey = await Storage.getStringList(saveBluePublicKey); - final List getPublicKeyList = changeStringListToIntList(publicKey!); + final List? publicKey = + await Storage.getStringList(saveBluePublicKey); + final List getPublicKeyList = + changeStringListToIntList(publicKey!); String weekStr = '00000000'; for (var day in state.weekDays.value) { - final int index = day % 7; // 将周日的索引转换为 0 - weekStr = '${weekStr.substring(0, index)}1${weekStr.substring(index + 1)}'; + final int index = day % 7; // 将周日的索引转换为 0 + weekStr = + '${weekStr.substring(0, index)}1${weekStr.substring(index + 1)}'; } // 倒序 weekStr weekStr = weekStr.split('').reversed.join(''); final int number = int.parse(weekStr, radix: 2); final List list = []; - list.add(state.isOpenNormallyOpenMode.value == true ? 1:0); + list.add(state.isOpenNormallyOpenMode.value == true ? 1 : 0); final int bieginTime = state.beginTimeMinute.value; final double bieginDouble = bieginTime / 256; @@ -159,7 +172,7 @@ class NormallyOpenModeLogic extends BaseGetXController{ list.add(end1); list.add(end2); - list.add(state.isAllDay.value == 1 ? 1:0); + list.add(state.isAllDay.value == 1 ? 1 : 0); list.add(number); list.add(0); @@ -177,7 +190,7 @@ class NormallyOpenModeLogic extends BaseGetXController{ dismissEasyLoading(); cancelBlueConnetctToastTimer(); state.sureBtnState.value = 0; - if(state.ifCurrentScreen.value == true){ + if (state.ifCurrentScreen.value == true) { showBlueConnetctToast(); } } diff --git a/lib/main/lockDetail/palm/addPalm/addPalm_logic.dart b/lib/main/lockDetail/palm/addPalm/addPalm_logic.dart index 19ca548d..eb843a3a 100755 --- a/lib/main/lockDetail/palm/addPalm/addPalm_logic.dart +++ b/lib/main/lockDetail/palm/addPalm/addPalm_logic.dart @@ -90,6 +90,12 @@ class AddPalmLogic extends BaseGetXController { isBeforeAddUser: false ); break; + case 0xFE: + case 12: + // 管理员已满 + showToast('管理员已满'.tr); + Get.close(1); + break; default: //失败 state.ifAddState.value = false; @@ -111,6 +117,7 @@ class AddPalmLogic extends BaseGetXController { Get.close(1); break; case 0xFE: + case 12: // 管理员已满 showToast('管理员已满'.tr); Get.close(1); diff --git a/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart b/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart index 04059392..33b2f2f7 100755 --- a/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart +++ b/lib/main/lockDetail/passwordKey/passwordKeyDetail/passwordKeyDetail_logic.dart @@ -152,6 +152,13 @@ class PasswordKeyDetailLogic extends BaseGetXController { final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); + int startTime = 0; + int endTime = 0; + if (state.itemData.value.keyboardPwdType != 2) { + startTime = state.itemData.value.startDate! ~/ 1000; + endTime = state.itemData.value.endDate! ~/ 1000; + } + IoSenderManage.senderCustomPasswordsCommand( keyID: state.itemData.value.keyboardPwdId!.toString(), userID: await Storage.getUid(), @@ -162,8 +169,8 @@ class PasswordKeyDetailLogic extends BaseGetXController { ? (state.isDeletPasswordKey.value == true ? 2 : 1) : 3, isAdmin: state.isAdministrator.value == true ? 1 : 0, - startTime: state.itemData.value.startDate! ~/ 1000, - endTime: state.itemData.value.endDate! ~/ 1000, + startTime: startTime, + endTime: endTime, needAuthor: 1, isBeforeAddUser: false, signKey: signKeyDataList, diff --git a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart index 7916fd7e..35d5a10e 100644 --- a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart +++ b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart @@ -90,6 +90,13 @@ class AddRemoteControlLogic extends BaseGetXController{ isBeforeAddUser: false ); break; + case 0xFE: + case 12: + // 管理员已满 + showToast('管理员已满'.tr); + state.ifAddState.value = false; + Get.close(1); + break; default: //失败 state.ifAddState.value = false; @@ -110,6 +117,7 @@ class AddRemoteControlLogic extends BaseGetXController{ Get.close(1); break; case 0xFE: + case 0x12: // 管理员已满 showToast('管理员已满'.tr); Get.close(1); diff --git a/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart b/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart index 77872c3c..75d52cf4 100755 --- a/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart +++ b/lib/mine/addLock/nearbyLock/nearbyLock_logic.dart @@ -93,7 +93,6 @@ class NearbyLockLogic extends BaseGetXController { showToast('固件升级完成'.tr); closeOTADAta(); } - dismissEasyLoading(); }); } @@ -160,6 +159,7 @@ class NearbyLockLogic extends BaseGetXController { // 获取星锁状态 Future _replyGetStarLockStatusInfo(Reply reply) async { final int status = reply.data[2]; + dismissEasyLoading(); switch (status) { case 0x00: //成功 @@ -449,50 +449,6 @@ class NearbyLockLogic extends BaseGetXController { } } - /// 判断 128-bit UUID 是否已配对 - bool isPaired128Bit(String serviceUuid) { - if (serviceUuid.length != 36) return false; // 确保是 128-bit UUID - try { - String status = serviceUuid.substring(30, 32); // 获取第 31 和 32 位 - return status == '01'; // '01' 表示已配对 - } catch (e) { - return false; // 如果索引越界或其他错误,返回 false - } - } - - /// 判断 128-bit UUID 是否休眠 - bool isSleeping128Bit(String serviceUuid) { - if (serviceUuid.length != 36) return false; // 确保是 128-bit UUID - try { - String status = serviceUuid.substring(32, 34); // 获取第 33 和 34 位 - return status == '00'; // '00' 表示休眠 - } catch (e) { - return false; // 如果索引越界或其他错误,返回 false - } - } - - /// 判断 32-bit UUID 是否已配对 - bool isPaired32Bit(String serviceUuid) { - if (serviceUuid.length != 8) return false; // 确保是 32-bit UUID - try { - String status = serviceUuid.substring(3, 5); // 获取第 4 和 5 位 - return status == '01'; // '01' 表示已配对 - } catch (e) { - return false; // 如果索引越界或其他错误,返回 false - } - } - - /// 判断 32-bit UUID 是否休眠 - bool isSleeping32Bit(String serviceUuid) { - if (serviceUuid.length != 8) return false; // 确保是 32-bit UUID - try { - String status = serviceUuid.substring(5, 7); // 获取第 6 和 7 位 - return status == '00'; // '00' 表示休眠 - } catch (e) { - return false; // 如果索引越界或其他错误,返回 false - } - } - void stopScanBlueList() { BlueManage().disconnect(); BlueManage().stopScan(); @@ -743,8 +699,8 @@ class NearbyLockLogic extends BaseGetXController { @override void onClose() { - super.onClose(); _replySubscription?.cancel(); + super.onClose(); } Future getNearByLimits() async { diff --git a/lib/mine/mineSet/mineSet/mineSet_page.dart b/lib/mine/mineSet/mineSet/mineSet_page.dart index a098bb8b..a2926a55 100755 --- a/lib/mine/mineSet/mineSet/mineSet_page.dart +++ b/lib/mine/mineSet/mineSet/mineSet_page.dart @@ -456,10 +456,10 @@ class _MineSetPageState extends State // showLoginOutAlertTipDialog(); }), Container( - padding: EdgeInsets.only(right: 30.w, top: 30.h), + padding: EdgeInsets.only(left: 30.w, top: 30.h), // color: Colors.red, child: Row( - mainAxisAlignment: MainAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, children: [ TextButton( child: Text( diff --git a/lib/talk/starChart/constant/talk_constant.dart b/lib/talk/starChart/constant/talk_constant.dart index a3847d50..60d57a0d 100644 --- a/lib/talk/starChart/constant/talk_constant.dart +++ b/lib/talk/starChart/constant/talk_constant.dart @@ -1,7 +1,19 @@ +import 'package:star_lock/talk/starChart/proto/talk_expect.pb.dart'; + class TalkConstant { // TalkPing 未收到回复超时时间(s) static const int talkePingOverTime = 10; static const int talkeDataOverTime = 10; + // 收到TalkRequest 未处理超时时间(s) static const int talkeRequestOverTime = 30; + + static TalkExpectReq ImageExpect = TalkExpectReq( + videoType: [VideoTypeE.IMAGE], + audioType: [AudioTypeE.G711], + ); + static TalkExpectReq H264Expect = TalkExpectReq( + videoType: [VideoTypeE.H264], + audioType: [AudioTypeE.G711], + ); } diff --git a/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart index e2caed39..0a9e7ab3 100644 --- a/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/starChart/handle/impl/udp_talk_data_handler.dart @@ -4,6 +4,7 @@ import 'package:star_lock/talk/call/g711.dart'; import 'package:star_lock/talk/starChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/starChart/entity/scp_message.dart'; import 'package:star_lock/talk/starChart/handle/other/h264_frame_handler.dart'; +import 'package:star_lock/talk/starChart/handle/other/packet_loss_statistics.dart'; import 'package:star_lock/talk/starChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/starChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/starChart/proto/talk_data.pb.dart'; @@ -14,62 +15,6 @@ import 'package:star_lock/talk/starChart/proto/talk_data_h264_frame.pb.dart'; // implements ScpMessageHandler { class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandler { - factory UdpTalkDataHandler() { - return _instance; - } - - UdpTalkDataHandler._internal(); - - static final UdpTalkDataHandler _instance = UdpTalkDataHandler._internal(); - - int _recentRecvDataRate = 0; - int _recentRecvPacketCount = 0; - int _recentSendDataRate = 0; - int _recentSendPacketCount = 0; - - int _lastRecvDataRate = 0; - int _lastRecvPacketCount = 0; - int _lastSendDataRate = 0; - int _lastSendPacketCount = 0; - - void updateRecvDataRate(int dataSize) { - _recentRecvDataRate += dataSize; - _recentRecvPacketCount++; - } - - void updateSendDataRate(int dataSize) { - _recentSendDataRate += dataSize; - _recentSendPacketCount++; - } - - void resetDataRates() { - _lastRecvDataRate = _recentRecvDataRate; - _lastRecvPacketCount = _recentRecvPacketCount; - _lastSendDataRate = _recentSendDataRate; - _lastSendPacketCount = _recentSendPacketCount; - - _recentRecvDataRate = 0; - _recentRecvPacketCount = 0; - _recentSendDataRate = 0; - _recentSendPacketCount = 0; - } - - int getLastRecvDataRate() { - return _lastRecvDataRate; - } - - int getLastRecvPacketCount() { - return _lastRecvPacketCount; - } - - int getLastSendDataRate() { - return _lastSendDataRate; - } - - int getLastSendPacketCount() { - return _lastSendPacketCount; - } - @override void handleReq(ScpMessage scpMessage) {} @@ -95,6 +40,17 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle return buffer.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); } + // 在类顶部添加异步日志方法 + void _asyncLog(String message) { + Future.microtask(() { + try { + AppLog.log(message); + } catch (e) { + // 错误处理 + } + }); + } + @override deserializePayload( {required int payloadType, @@ -105,10 +61,12 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle int? spTotal, int? spIndex, int? messageId}) { - // AppLog.log( - // '没有组包之前的每一个包的数据:${byte.length} messageId:$messageId spTotal:$spTotal spIndex:$spIndex PayloadLength:$PayloadLength,byte:${bufferToHexString(byte)}'); + // 获取统计信息 + final stats = PacketLossStatistics().getStatistics(); + _asyncLog('丢包统计: $stats'); + // _asyncLog( + // '分包数据:messageId:$messageId [$spIndex/$spTotal] PayloadLength:$PayloadLength'); if (messageType == MessageTypeConstant.RealTimeData) { - // 回声测试 if (spTotal != null && spTotal > 1 && messageId != null && @@ -161,8 +119,6 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle final TalkDataH264Frame talkDataH264Frame = TalkDataH264Frame(); talkDataH264Frame.mergeFromBuffer(talkData.content); frameHandler.handleFrame(talkDataH264Frame); - // AppLog.log( - // "帧:${talkDataH264Frame.frameType},帧序号:${talkDataH264Frame.frameSeq},对应I帧序号:${talkDataH264Frame.frameSeqI}"); } /// 处理图片数据 diff --git a/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart index 06b79ad6..be2f4565 100644 --- a/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart @@ -25,8 +25,24 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle RxString currentLanguage = CurrentLocaleTool.getCurrentLocaleString().obs; // 当前选择语言 + // 添加上次处理请求的时间戳 + int _lastRequestTime = 0; + @override void handleReq(ScpMessage scpMessage) async { + + final currentTime = DateTime.now().millisecondsSinceEpoch; + // 确保与上次请求间隔至少1秒 + if (currentTime - _lastRequestTime < 1000) { + // 如果间隔小于1秒,直接拒绝请求 + replyErrorMessage(scpMessage); + AppLog.log('对讲请求过于频繁,已拒绝'); + return; + } + + // 更新最后处理时间 + _lastRequestTime = currentTime; + // 判断是否登录账户 final loginData = await Storage.getLoginData(); @@ -47,6 +63,7 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle } else { // 拒绝接听,回复失败 replyErrorMessage(scpMessage); + AppLog.log('正在接听,回复拒绝'); } } diff --git a/lib/talk/starChart/handle/other/h264_frame_handler.dart b/lib/talk/starChart/handle/other/h264_frame_handler.dart index 2b4ea1ee..cd2f8bc2 100644 --- a/lib/talk/starChart/handle/other/h264_frame_handler.dart +++ b/lib/talk/starChart/handle/other/h264_frame_handler.dart @@ -1,105 +1,17 @@ import 'dart:collection'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import '../../proto/talk_data_h264_frame.pb.dart'; class H264FrameHandler { - final LinkedHashMap _frameBuffer = LinkedHashMap(); - final void Function(List frameData) onCompleteFrame; - final LinkedHashMap _frameTypeIndex = LinkedHashMap(); + final void Function(List frameData) onCompleteFrame; H264FrameHandler({required this.onCompleteFrame}); void handleFrame(TalkDataH264Frame frame) { - // 存储帧 - _frameBuffer[frame.frameSeq] = frame; - _frameTypeIndex[frame.frameSeq] = frame.frameType; - - // 检查是否可以组装完整的 GOP (Group of Pictures) - _tryAssembleFrames(frame.frameSeq); - } - - void _tryAssembleFrames(int currentSeq) { - final List framesToProcess = []; - int? startFrameSeq; - - // 从当前帧开始向前找到最近的 I 帧或 P 帧 - for (int seq = currentSeq; seq >= 0; seq--) { - final frameType = _frameTypeIndex[seq]; - if (frameType == null) continue; - if (frameType == TalkDataH264Frame_FrameTypeE.I) { - startFrameSeq = seq; - break; - } else if (frameType == TalkDataH264Frame_FrameTypeE.P) { - if (_frameBuffer.containsKey(_frameBuffer[seq]!.frameSeqI)) { - startFrameSeq = seq; - break; - } else { - _frameBuffer.remove(seq); - _frameTypeIndex.remove(seq); - } - } - } - - if (startFrameSeq != null) { - for (int seq = startFrameSeq; _frameBuffer.containsKey(seq); seq++) { - framesToProcess.add(seq); - } - - if (framesToProcess.isNotEmpty) { - _processFrames(framesToProcess); - } - } else { - _clearOldFrames(currentSeq); - } - } - - void _processFrames(List frameSeqs) { - // 按顺序组装帧数据 - // final List assembledData = []; - // - // for (var seq in frameSeqs) { - // final frame = _frameBuffer[seq]!; - // assembledData.addAll(frame.frameData); - // - // // 处理完后从缓冲区移除 - // _frameBuffer.remove(seq); - // } - // - // // 回调完整的帧数据 - // onCompleteFrame(assembledData); - // Calculate the total length of the assembled data - int totalLength = frameSeqs.fold( - 0, (sum, seq) => sum + _frameBuffer[seq]!.frameData.length); - - // Allocate a buffer for the assembled data - final assembledData = Uint8List(totalLength); - int offset = 0; - - for (var seq in frameSeqs) { - final frame = _frameBuffer[seq]!; - assembledData.setRange( - offset, offset + frame.frameData.length, frame.frameData); - offset += frame.frameData.length; - - // Remove the frame from the buffer after processing - _frameBuffer.remove(seq); - _frameTypeIndex.remove(seq); - } - - // Callback with the complete frame data - onCompleteFrame(assembledData); - } - - void clear() { - _frameBuffer.clear(); - } - - void _clearOldFrames(int currentSeq) { - // 清理比当前帧序列旧的帧 - _frameBuffer.removeWhere((seq, frame) => seq < currentSeq - 200); // 调整阈值 - _frameTypeIndex.removeWhere((seq, frameType) => seq < currentSeq - 200); + onCompleteFrame(frame.frameData); } } diff --git a/lib/talk/starChart/handle/other/packet_loss_statistics.dart b/lib/talk/starChart/handle/other/packet_loss_statistics.dart new file mode 100644 index 00000000..34dd2282 --- /dev/null +++ b/lib/talk/starChart/handle/other/packet_loss_statistics.dart @@ -0,0 +1,94 @@ +import 'dart:collection'; + +class PacketLossStatistics { + static final PacketLossStatistics _instance = + PacketLossStatistics._internal(); + factory PacketLossStatistics() => _instance; + PacketLossStatistics._internal(); + + // 记录每个messageId的分包信息 + // key: messageId, value: {totalPackets, receivedPackets} + final Map _packetsMap = HashMap(); + + // 统计信息 + int _totalMessages = 0; // 总消息数 + int _lostMessages = 0; // 丢包的消息数 + int _totalPackets = 0; // 总分包数 + int _lostPackets = 0; // 丢失的分包数 + + // 记录分包数据 + void recordPacket(int messageId, int currentIndex, int totalPackets) { + if (!_packetsMap.containsKey(messageId)) { + _packetsMap[messageId] = PacketInfo(totalPackets); + _totalMessages++; + _totalPackets += totalPackets; + } + + _packetsMap[messageId]!.receivedPackets.add(currentIndex); + + // 如果收到了该messageId的最后一个包,进行统计 + if (currentIndex == totalPackets) { + _checkPacketLoss(messageId); + } + } + + // 检查丢包情况 + void _checkPacketLoss(int messageId) { + final info = _packetsMap[messageId]!; + + // 检查是否有丢失的包 + int received = info.receivedPackets.length; + if (received < info.totalPackets) { + _lostMessages++; + _lostPackets += (info.totalPackets - received); + } + + // 清理该messageId的记录,避免内存泄漏 + _packetsMap.remove(messageId); + } + + // 获取丢包率统计信息 + PacketLossInfo getStatistics() { + if (_totalMessages == 0 || _totalPackets == 0) { + return PacketLossInfo(0.0, 0.0); + } + + // 计算消息级别的丢包率 + double messageLossRate = (_lostMessages / _totalMessages) * 100; + + // 计算分包级别的丢包率 + double packetLossRate = (_lostPackets / _totalPackets) * 100; + + return PacketLossInfo(messageLossRate, packetLossRate); + } + + // 重置统计数据 + void reset() { + _packetsMap.clear(); + _totalMessages = 0; + _lostMessages = 0; + _totalPackets = 0; + _lostPackets = 0; + } +} + +// 分包信息类 +class PacketInfo { + final int totalPackets; + final Set receivedPackets = HashSet(); + + PacketInfo(this.totalPackets); +} + +// 丢包统计信息类 +class PacketLossInfo { + final double messageLossRate; // 消息丢失率 + final double packetLossRate; // 分包丢失率 + + PacketLossInfo(this.messageLossRate, this.packetLossRate); + + @override + String toString() { + return 'Message Loss Rate: ${messageLossRate.toStringAsFixed(2)}%, Packet Loss Rate: ${packetLossRate.toStringAsFixed(2)}%'; + } +} diff --git a/lib/talk/starChart/handle/other/talk_data_repository.dart b/lib/talk/starChart/handle/other/talk_data_repository.dart index 864aa4aa..062835cb 100644 --- a/lib/talk/starChart/handle/other/talk_data_repository.dart +++ b/lib/talk/starChart/handle/other/talk_data_repository.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:star_lock/talk/starChart/proto/talk_data.pb.dart'; class TalkDataRepository { - // 创建一个私有的构造函数,防止外部创建实例 TalkDataRepository._() { _talkDataStreamController = StreamController.broadcast( onListen: () { @@ -11,47 +10,26 @@ class TalkDataRepository { onCancel: () { _isListening = false; }, - sync: false, // 异步模式 + sync: false, // 改为同步模式以提高实时性 ); } - // 使用 _instance 来保存单例对象 static final TalkDataRepository _instance = TalkDataRepository._(); - // 提供一个静态方法来获取单例实例 static TalkDataRepository get instance => _instance; - // 创建一个 StreamController late final StreamController _talkDataStreamController; - bool _isListening = false; - // 用于存储数据的缓冲区 - final List _buffer = []; + // 直接返回原始流,不做转换 + Stream get talkDataStream => _talkDataStreamController.stream; - // 提供一个方法来获取 Stream - Stream get talkDataStream => - _talkDataStreamController.stream.transform( - StreamTransformer.fromHandlers( - handleData: (TalkData data, EventSink sink) { - // 限制缓冲区大小为 100 - if (_buffer.length >= 100) { - _buffer.removeAt(0); // 丢弃最旧的数据 - } - _buffer.add(data); - sink.add(data); - }, - ), - ); - - // 提供一个方法来添加 TalkData 到 Stream void addTalkData(TalkData talkData) { if (_isListening) { _talkDataStreamController.add(talkData); } } - // 提供一个方法来关闭 StreamController void dispose() { _talkDataStreamController.close(); } diff --git a/lib/talk/starChart/star_chart_manage.dart b/lib/talk/starChart/star_chart_manage.dart index 7f60fc25..7ffed8e5 100644 --- a/lib/talk/starChart/star_chart_manage.dart +++ b/lib/talk/starChart/star_chart_manage.dart @@ -22,6 +22,7 @@ import 'package:star_lock/talk/starChart/constant/ip_constant.dart'; import 'package:star_lock/talk/starChart/constant/listen_addr_type_constant.dart'; import 'package:star_lock/talk/starChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/starChart/constant/payload_type_constant.dart'; +import 'package:star_lock/talk/starChart/constant/talk_constant.dart'; import 'package:star_lock/talk/starChart/constant/talk_status.dart'; import 'package:star_lock/talk/starChart/entity/relay_info_entity.dart'; import 'package:star_lock/talk/starChart/entity/report_information_data.dart'; @@ -31,6 +32,7 @@ import 'package:star_lock/talk/starChart/exception/start_chart_message_exception import 'package:star_lock/talk/starChart/handle/impl/debug_Info_model.dart'; import 'package:star_lock/talk/starChart/handle/impl/udp_talk_data_handler.dart'; import 'package:star_lock/talk/starChart/handle/other/do_sign.dart'; +import 'package:star_lock/talk/starChart/handle/other/packet_loss_statistics.dart'; import 'package:star_lock/talk/starChart/handle/other/talke_data_over_time_timer_manager.dart'; import 'package:star_lock/talk/starChart/handle/other/talke_ping_over_time_timer_manager.dart'; import 'package:star_lock/talk/starChart/handle/other/talke_request_over_time_timer_manager.dart'; @@ -111,10 +113,7 @@ class StartChartManage { final int _maxPayloadSize = 8 * 1024; // 分包大小 // 默认通话的期望数据格式 - TalkExpectReq _defaultTalkExpect = TalkExpectReq( - videoType: [VideoTypeE.IMAGE], - audioType: [AudioTypeE.G711], - ); + TalkExpectReq _defaultTalkExpect = TalkConstant.ImageExpect; String relayPeerId = ''; // 中继peerId @@ -342,7 +341,7 @@ class StartChartManage { } } - // 发送RbcuConfirm 打洞确认 + // 发送打洞确认包 void _sendRbcuConfirmMessage() async { RbcuConfirm( sessionId: _rbcuSessionId, @@ -595,27 +594,39 @@ class StartChartManage { // 发送拒绝接听消息 void startTalkRejectMessageTimer() async { - talkRejectTimer ??= Timer.periodic( - Duration(seconds: _defaultIntervalTime), - (Timer timer) async { - _sendTalkRejectMessage(); - }, - ); + try { + int count = 0; + final int maxCount = 10; // 最大执行次数为10秒 - // 设置状态为拒绝 - StartChartTalkStatus.instance.setRejected(); - // 停止播放铃声 - AudioPlayerManager().stopRingtone(); - // 停止发送通话保持消息、通话预期数据请求 - stopTalkExpectMessageTimer(); - stopTalkPingMessageTimer(); - stopCallRequestMessageTimer(); - stopSendingRbcuInfoMessages(); - stopSendingRbcuProBeMessages(); - // 取消定时器 + talkRejectTimer ??= Timer.periodic( + Duration(seconds: _defaultIntervalTime), + (Timer timer) async { + _sendTalkRejectMessage(); + count++; + if (count >= maxCount) { + timer.cancel(); + talkRejectTimer = null; + } + }, + ); + } catch (e) { + AppLog.log("startTalkRejectMessageTimer e:${e}"); + } finally { + // 设置状态为拒绝 + StartChartTalkStatus.instance.setRejected(); + // 停止播放铃声 + AudioPlayerManager().stopRingtone(); + // 停止发送通话保持消息、通话预期数据请求 + stopTalkExpectMessageTimer(); + stopTalkPingMessageTimer(); + stopCallRequestMessageTimer(); + stopSendingRbcuInfoMessages(); + stopSendingRbcuProBeMessages(); + // 取消定时器 - talkePingOverTimeTimerManager.cancel(); - talkDataOverTimeTimerManager.cancel(); + talkePingOverTimeTimerManager.cancel(); + talkDataOverTimeTimerManager.cancel(); + } } // 发送期望接受消息 @@ -1031,6 +1042,14 @@ class StartChartManage { final int payloadType = scpMessage.PayloadType ?? 0; final int messageType = scpMessage.MessageType ?? 0; try { + // 记录分包数据用于统计丢包率 + if (scpMessage.SpIndex != null && + scpMessage.SpTotal != null && + scpMessage.MessageId != null) { + PacketLossStatistics().recordPacket( + scpMessage.MessageId!, scpMessage.SpIndex!, scpMessage.SpTotal!); + } + final ScpMessageHandler handler = ScpMessageHandlerFactory.createHandler(payloadType); if (messageType == MessageTypeConstant.Req) { @@ -1126,10 +1145,7 @@ class StartChartManage { } void reSetDefaultTalkExpect() { - _defaultTalkExpect = TalkExpectReq( - videoType: [VideoTypeE.IMAGE], - audioType: [AudioTypeE.G711], - ); + _defaultTalkExpect = TalkConstant.ImageExpect; } TalkExpectReq getDefaultTalkExpect() { @@ -1148,10 +1164,7 @@ class StartChartManage { /// 修改预期接收到的数据 void sendImageVideoAndG711AudioTalkExpectData() { - final talkExpectReq = TalkExpectReq( - videoType: [VideoTypeE.IMAGE], - audioType: [AudioTypeE.G711], - ); + final talkExpectReq = TalkConstant.ImageExpect; changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer( talkExpect: talkExpectReq); } @@ -1212,6 +1225,7 @@ class StartChartManage { await Storage.removerStarChartRegisterNodeInfo(); // 关闭udp服务 closeUdpSocket(); + PacketLossStatistics().reset(); } /// 重置数据 diff --git a/lib/talk/starChart/status/appLifecycle_observer.dart b/lib/talk/starChart/status/appLifecycle_observer.dart index 03a0a37f..37b70152 100644 --- a/lib/talk/starChart/status/appLifecycle_observer.dart +++ b/lib/talk/starChart/status/appLifecycle_observer.dart @@ -28,17 +28,16 @@ class AppLifecycleObserver extends WidgetsBindingObserver { void onAppPaused() { // 处理应用程序进入后台的逻辑 - print('App has entered the background.'); - if (StartChartManage().talkStatus.status == - TalkStatus.passiveCallWaitingAnswer || - StartChartManage().talkStatus.status == - TalkStatus.proactivelyCallWaitingAnswer) { - StartChartManage().startTalkHangupMessageTimer(); - StartChartManage().startTalkRejectMessageTimer(); - // 如果是等待接听时就退出页面 + + final status = StartChartManage().talkStatus.status; + + if (status == TalkStatus.passiveCallWaitingAnswer || + status == TalkStatus.proactivelyCallWaitingAnswer || + status == TalkStatus.answeredSuccessfully || + status == TalkStatus.uninitialized) { + StartChartManage().destruction(); Get.back(); } - StartChartManage().destruction(); } void onAppResumed() async { diff --git a/lib/talk/starChart/views/talkView/talk_view_logic.dart b/lib/talk/starChart/views/talkView/talk_view_logic.dart index 8c0e3737..779e44e9 100644 --- a/lib/talk/starChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/starChart/views/talkView/talk_view_logic.dart @@ -38,8 +38,8 @@ class TalkViewLogic extends BaseGetXController { final LockDetailState lockDetailState = Get.put(LockDetailLogic()).state; final int minBufferSize = 2; // 最小缓冲2帧,约166ms - final int maxBufferSize = 8; // 最大缓冲8帧,约666ms - int bufferSize = 3; // 初始化为默认大小 + final int maxBufferSize = 20; // 最大缓冲8帧,约666ms + int bufferSize = 8; // 初始化为默认大小 // 修改音频相关的成员变量 final int minAudioBufferSize = 1; // 音频最小缓冲1帧 final int maxAudioBufferSize = 3; // 音频最大缓冲3帧 @@ -56,6 +56,9 @@ class TalkViewLogic extends BaseGetXController { final Map _imageCache = {}; + // 添加一个变量用于记录上一帧的时间戳 + int _lastFrameTimestamp = 0; // 初始值为 0 + // 添加帧率计算相关变量 int _frameCount = 0; int _lastFpsUpdateTime = 0; @@ -100,11 +103,11 @@ class TalkViewLogic extends BaseGetXController { // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: - // 第一帧到达时记录开始时间 - if (_isFirstAudioFrame) { - _startAudioTime = currentTime; - _isFirstAudioFrame = false; - } + // // 第一帧到达时记录开始时间 + // if (_isFirstAudioFrame) { + // _startAudioTime = currentTime; + // _isFirstAudioFrame = false; + // } // 计算音频延迟 final expectedTime = _startAudioTime + talkData.durationMs; @@ -130,15 +133,16 @@ class TalkViewLogic extends BaseGetXController { if (_isFirstFrame) { _startTime = currentTime; _isFirstFrame = false; - // AppLog.log('记录第一帧的时间戳${currentTime},${talkData.durationMs}'); + AppLog.log('第一帧帧的时间戳:${talkData.durationMs}'); } + // AppLog.log('其他帧的时间戳:${talkData.durationMs}'); + // 计算帧间间隔 + if (_lastFrameTimestamp != 0) { + final int frameInterval = talkData.durationMs - _lastFrameTimestamp; + _adjustBufferSize(frameInterval); // 根据帧间间隔调整缓冲区 + } + _lastFrameTimestamp = talkData.durationMs; // 更新上一帧时间戳 - // 计算实际延迟:当前时间 - 预期播放时间 - final expectedTime = _startTime + talkData.durationMs; - final videoDelay = currentTime - expectedTime; // 修改延迟计算方式 - - // 动态调整缓冲区 - _adjustBufferSize(videoDelay); // 然后添加到播放缓冲区 if (state.videoBuffer.length >= bufferSize) { state.videoBuffer.removeAt(0); @@ -180,20 +184,18 @@ class TalkViewLogic extends BaseGetXController { state.listData.value = Uint8List.fromList(oldestFrame.content); state.videoBuffer.removeAt(oldestIndex); // 移除已播放的帧 - // 更新帧率计算 - _frameCount++; - final currentTime = DateTime.now().millisecondsSinceEpoch; - final elapsed = currentTime - _lastFpsUpdateTime; + // // 更新帧率计算 + // _frameCount++; + // final currentTime = DateTime.now().millisecondsSinceEpoch; + // final elapsed = currentTime - _lastFpsUpdateTime; + // + // if (elapsed >= 1000) { + // // 每秒更新一次 + // state.fps.value = (_frameCount * 1000 / elapsed).round(); + // _frameCount = 0; + // _lastFpsUpdateTime = currentTime; + // } - if (elapsed >= 1000) { - // 每秒更新一次 - state.fps.value = (_frameCount * 1000 / elapsed).round(); - _frameCount = 0; - _lastFpsUpdateTime = currentTime; - } - // AppLog.log('🎬 播放帧 - 缓冲区剩余: ${state.videoBuffer.length}/${bufferSize}, ' - // '播放延迟: ${currentTime - oldestFrame.durationMs}ms, ' - // '帧时间戳: ${oldestFrame.durationMs}'); } else { // AppLog.log('⚠️ 帧未找到缓存 - Key: $cacheKey'); state.videoBuffer.removeAt(oldestIndex); // 移除无法播放的帧 @@ -257,19 +259,21 @@ class TalkViewLogic extends BaseGetXController { } // 新增:动态调整缓冲区大小的方法 - void _adjustBufferSize(int delay) { - const int delayThresholdHigh = 250; // 高延迟阈值(约3帧的时间) - const int delayThresholdLow = 166; // 低延迟阈值(约2帧的时间) + void _adjustBufferSize(int frameInterval) { + const int frameDuration = 83; // 假设每帧的时间间隔为 83ms(12fps) + const int delayThresholdHigh = frameDuration * 2; // 高延迟阈值(2帧时间) + const int delayThresholdLow = frameDuration; // 低延迟阈值(1帧时间) const int adjustInterval = 1; // 每次调整1帧 - if (delay > delayThresholdHigh && bufferSize < maxBufferSize) { - // 延迟较大,增加缓冲区 + if (frameInterval > delayThresholdHigh && bufferSize < maxBufferSize) { + // 帧间间隔较大,增加缓冲区 bufferSize = min(bufferSize + adjustInterval, maxBufferSize); - // AppLog.log('📈 增加缓冲区 - 当前大小: $bufferSize, 延迟: ${delay}ms'); - } else if (delay < delayThresholdLow && bufferSize > minBufferSize) { - // 延迟较小,减少缓冲区 + AppLog.log('📈 增加缓冲区 - 当前大小: $bufferSize, 帧间间隔: ${frameInterval}ms'); + } else if (frameInterval < delayThresholdLow && + bufferSize > minBufferSize) { + // 帧间间隔较小,减少缓冲区 bufferSize = max(bufferSize - adjustInterval, minBufferSize); - // AppLog.log('📉 减少缓冲区 - 当前大小: $bufferSize, 延迟: ${delay}ms'); + AppLog.log('📉 减少缓冲区 - 当前大小: $bufferSize, 帧间间隔: ${frameInterval}ms'); } } @@ -493,6 +497,7 @@ class TalkViewLogic extends BaseGetXController { _initAudioRecorder(); requestPermissions(); + } @override diff --git a/lib/talk/starChart/views/talkView/talk_view_page.dart b/lib/talk/starChart/views/talkView/talk_view_page.dart index 9492bc59..38b22343 100644 --- a/lib/talk/starChart/views/talkView/talk_view_page.dart +++ b/lib/talk/starChart/views/talkView/talk_view_page.dart @@ -615,7 +615,7 @@ class _TalkViewPageState extends State state.videoBuffer.clear(); state.listData.value = Uint8List(0); CallTalk().finishAVData(); - UdpTalkDataHandler().resetDataRates(); + // UdpTalkDataHandler().resetDataRates(); super.dispose(); } } diff --git a/lib/talk/starChart/webView/h264_web_logic.dart b/lib/talk/starChart/webView/h264_web_logic.dart index 4b7edc5e..fdbaad3f 100644 --- a/lib/talk/starChart/webView/h264_web_logic.dart +++ b/lib/talk/starChart/webView/h264_web_logic.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:io'; import 'dart:ui' as ui; import 'dart:math'; // Import the math package to use sqrt @@ -23,11 +24,13 @@ import 'package:star_lock/main/lockMian/entity/lockListInfo_entity.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/talk/call/g711.dart'; import 'package:star_lock/talk/starChart/constant/talk_status.dart'; +import 'package:star_lock/talk/starChart/handle/other/packet_loss_statistics.dart'; import 'package:star_lock/talk/starChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/starChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/starChart/star_chart_manage.dart'; import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart'; import 'package:star_lock/talk/starChart/webView/h264_web_view_state.dart'; +import 'package:star_lock/tools/G711Tool.dart'; import 'package:star_lock/tools/bugly/bugly_tool.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -38,6 +41,14 @@ class H264WebViewLogic extends BaseGetXController { final LockDetailState lockDetailState = Get.put(LockDetailLogic()).state; + // 添加模拟数据相关变量 + static const int CHUNK_SIZE = 4096; + Timer? _mockDataTimer; + // 定义音频帧缓冲和发送函数 + final List _bufferedAudioFrames = []; + final Queue> _frameBuffer = Queue>(); + static const int FRAME_BUFFER_SIZE = 25; + @override void onInit() { super.onInit(); @@ -57,7 +68,7 @@ class H264WebViewLogic extends BaseGetXController { _loadLocalHtml(); // 创建流数据监听 _createFramesStreamListen(); - + // playLocalTestVideo(); _startListenTalkStatus(); state.talkStatus.value = state.startChartTalkStatus.status; // 初始化音频播放器 @@ -86,11 +97,43 @@ class H264WebViewLogic extends BaseGetXController { void _createFramesStreamListen() async { state.talkDataRepository.talkDataStream.listen((TalkData event) async { - // 发送数据给js处理 - _sendBufferedData(event.content); + // 添加新帧到缓冲区 + _frameBuffer.add(event.content); + + // 当缓冲区超过最大容量时,发送最早的帧并移除 + while (_frameBuffer.length > FRAME_BUFFER_SIZE) { + if (_frameBuffer.isNotEmpty) { + final frame = _frameBuffer.removeFirst(); + await _sendBufferedData(frame); + } + } }); } + /// 播放本地测试视频文件 + // Future playLocalTestVideo() async { + // try { + // ByteData data = await rootBundle.load('assets/html/demo.h264'); + // List bytes = data.buffer.asUint8List(); + // + // int offset = 0; + // _mockDataTimer = Timer.periodic(Duration(milliseconds: 40), (timer) { + // if (offset >= bytes.length) { + // timer.cancel(); + // return; + // } + // + // int end = min(offset + CHUNK_SIZE, bytes.length); + // List chunk = bytes.sublist(offset, end); + // _sendBufferedData(chunk); + // + // offset += CHUNK_SIZE; + // }); + // } catch (e) { + // AppLog.log('加载测试视频文件失败: $e'); + // } + // } + /// 加载html文件 Future _loadLocalHtml() async { // 加载 HTML 文件内容 @@ -226,15 +269,17 @@ class H264WebViewLogic extends BaseGetXController { //开始录音 Future startProcessingAudio() async { - // 增加录音帧监听器和错误监听器 - state.voiceProcessor?.addFrameListener(_onFrame); - state.voiceProcessor?.addErrorListener(_onError); try { if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) { await state.voiceProcessor?.start(state.frameLength, state.sampleRate); final bool? isRecording = await state.voiceProcessor?.isRecording(); state.isRecordingAudio.value = isRecording!; state.startRecordingAudioTime.value = DateTime.now(); + + // 增加录音帧监听器和错误监听器 + state.voiceProcessor + ?.addFrameListeners([_onFrame]); + state.voiceProcessor?.addErrorListener(_onError); } else { // state.errorMessage.value = 'Recording permission not granted'; } @@ -254,8 +299,8 @@ class H264WebViewLogic extends BaseGetXController { state.endRecordingAudioTime.value = DateTime.now(); // 计算录音的持续时间 - final duration = state.endRecordingAudioTime.value! - .difference(state.startRecordingAudioTime.value!); + final Duration duration = state.endRecordingAudioTime.value + .difference(state.startRecordingAudioTime.value); state.recordingAudioTime.value = duration.inSeconds; } on PlatformException catch (ex) { @@ -267,25 +312,71 @@ class H264WebViewLogic extends BaseGetXController { } } - // 音频帧处理 +// 音频帧处理 Future _onFrame(List frame) async { - // 预处理和转码操作放到异步计算线程 - // final processedFrame = await compute(preprocessAudio, frame); - // final list = listLinearToALaw(processedFrame); - final List processedFrame = preprocessAudio(frame); - final List list = listLinearToALaw(processedFrame); + // 添加最大缓冲限制 + if (_bufferedAudioFrames.length > state.frameLength * 3) { + _bufferedAudioFrames.clear(); // 清空过多积累的数据 + return; + } - final int ms = DateTime.now().millisecondsSinceEpoch - - state.startRecordingAudioTime.value.millisecondsSinceEpoch; + // 首先应用固定增益提升基础音量 + List amplifiedFrame = _applyGain(frame, 1.6); + // 编码为G711数据 + List encodedData = G711Tool.encode(amplifiedFrame, 0); // 0表示A-law + _bufferedAudioFrames.addAll(encodedData); + // 使用相对时间戳 + final int ms = DateTime.now().millisecondsSinceEpoch % 1000000; // 使用循环时间戳 + int getFrameLength = state.frameLength; + if (Platform.isIOS) { + getFrameLength = state.frameLength * 2; + } - // 发送音频数据到UDP - await StartChartManage().sendTalkDataMessage( - talkData: TalkData( - content: list, - contentType: TalkData_ContentTypeE.G711, - durationMs: ms, - ), - ); + // 添加发送间隔控制 + if (_bufferedAudioFrames.length >= state.frameLength) { + try { + await StartChartManage().sendTalkDataMessage( + talkData: TalkData( + content: _bufferedAudioFrames, + contentType: TalkData_ContentTypeE.G711, + durationMs: ms, + ), + ); + } finally { + _bufferedAudioFrames.clear(); // 确保清理缓冲区 + } + } else { + _bufferedAudioFrames.addAll(encodedData); + } + } + +// 错误监听 + void _onError(VoiceProcessorException error) { + AppLog.log(error.message!); + } + +// 添加音频增益处理方法 + List _applyGain(List pcmData, double gainFactor) { + List result = List.filled(pcmData.length, 0); + + for (int i = 0; i < pcmData.length; i++) { + // PCM数据通常是有符号的16位整数 + int sample = pcmData[i]; + + // 应用增益 + double amplified = sample * gainFactor; + + // 限制在有效范围内,防止溢出 + if (amplified > 32767) { + amplified = 32767; + } else if (amplified < -32768) { + amplified = -32768; + } + + result[i] = amplified.toInt(); + } + + return result; } /// 挂断 @@ -297,6 +388,9 @@ class H264WebViewLogic extends BaseGetXController { // 拒绝 StartChartManage().startTalkRejectMessageTimer(); } + // _mockDataTimer?.cancel(); + // _mockDataTimer = null; + PacketLossStatistics().reset(); Get.back(); } @@ -315,7 +409,7 @@ class H264WebViewLogic extends BaseGetXController { }); final LockSetInfoEntity lockSetInfoEntity = - await ApiRepository.to.getLockSettingInfoData( + await ApiRepository.to.getLockSettingInfoData( lockId: lockId.toString(), ); if (lockSetInfoEntity.errorCode!.codeIsSuccessful) { @@ -333,93 +427,11 @@ class H264WebViewLogic extends BaseGetXController { } } - List preprocessAudio(List pcmList) { - // 简单的降噪处理 - final List processedList = []; - for (int pcmVal in pcmList) { - // 简单的降噪示例:将小于阈值的信号置为0 - if (pcmVal.abs() < 200) { - pcmVal = 0; - } - processedList.add(pcmVal); - } - return processedList; - } - - List listLinearToALaw(List pcmList) { - final List aLawList = []; - for (int pcmVal in pcmList) { - final int aLawVal = linearToALaw(pcmVal); - aLawList.add(aLawVal); - } - return aLawList; - } - - int linearToALaw(int pcmVal) { - const int ALAW_MAX = 0x7FFF; // 32767 - const int ALAW_BIAS = 0x84; // 132 - - int mask; - int seg; - int aLawVal; - - // Handle sign - if (pcmVal < 0) { - pcmVal = -pcmVal; - mask = 0x7F; // 127 (sign bit is 1) - } else { - mask = 0xFF; // 255 (sign bit is 0) - } - - // Add bias and clamp to ALAW_MAX - pcmVal += ALAW_BIAS; - if (pcmVal > ALAW_MAX) { - pcmVal = ALAW_MAX; - } - - // Determine segment - seg = search(pcmVal); - - // Calculate A-law value - if (seg >= 8) { - aLawVal = 0x7F ^ mask; // Clamp to maximum value - } else { - int quantized = (pcmVal >> (seg + 3)) & 0xF; - aLawVal = (seg << 4) | quantized; - aLawVal ^= 0xD5; // XOR with 0xD5 to match standard A-law table - } - - return aLawVal; - } - - int search(int val) { - final List table = [ - 0xFF, // Segment 0 - 0x1FF, // Segment 1 - 0x3FF, // Segment 2 - 0x7FF, // Segment 3 - 0xFFF, // Segment 4 - 0x1FFF, // Segment 5 - 0x3FFF, // Segment 6 - 0x7FFF // Segment 7 - ]; - const int size = 8; - for (int i = 0; i < size; i++) { - if (val <= table[i]) { - return i; - } - } - return size; - } - -// 错误监听 - void _onError(VoiceProcessorException error) { - AppLog.log(error.message!); - } @override void dispose() { - // TODO: implement dispose + // _mockDataTimer?.cancel(); + // _mockDataTimer = null; super.dispose(); StartChartManage().startTalkHangupMessageTimer(); state.animationController.dispose(); @@ -429,5 +441,6 @@ class H264WebViewLogic extends BaseGetXController { state.oneMinuteTimeTimer = null; stopProcessingAudio(); StartChartManage().reSetDefaultTalkExpect(); + _frameBuffer.clear(); } } diff --git a/lib/tools/eventBusEventManage.dart b/lib/tools/eventBusEventManage.dart index 5d5e7416..810ad0a6 100755 --- a/lib/tools/eventBusEventManage.dart +++ b/lib/tools/eventBusEventManage.dart @@ -195,3 +195,12 @@ class RogerThatLockInfoDataEvent { class GetGatewayListRefreshUI { GetGatewayListRefreshUI(); } +/// 同意隐私协议 +class AgreePrivacyAgreement { + AgreePrivacyAgreement(); +} + +/// 配网成功 +class SuccessfulDistributionNetwork { + SuccessfulDistributionNetwork(); +}