fix:完善对讲流程、增加蓝牙透传消息指令
This commit is contained in:
parent
c16c9be4c6
commit
92fcf4a4fa
@ -12,7 +12,7 @@ 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/startChart/appLifecycle_observer.dart';
|
||||
import 'package:star_lock/talk/startChart/status/appLifecycle_observer.dart';
|
||||
import 'package:star_lock/tools/bugly/bugly_tool.dart';
|
||||
import 'package:star_lock/tools/device_info_service.dart';
|
||||
import 'package:star_lock/tools/platform_info_services.dart';
|
||||
|
||||
@ -21,7 +21,7 @@ class CatEyeSetLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
//设置自动亮屏
|
||||
//设置亮屏持续时间
|
||||
Future<void> updateLightScreenTimeConfig() async {
|
||||
final VersionUndateEntity entity = await ApiRepository.to.updateLightScreenTimeConfig(
|
||||
lockId: state.lockSetInfoData.value.lockId!,
|
||||
@ -112,4 +112,6 @@ class CatEyeSetLogic extends BaseGetXController {
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/blue/blue_manage.dart';
|
||||
import 'package:star_lock/blue/io_tool/io_tool.dart';
|
||||
import 'package:star_lock/blue/sender_manage.dart';
|
||||
import 'package:star_lock/common/XSConstantMacro/XSConstantMacro.dart';
|
||||
import 'package:star_lock/main/lockDetail/lockSet/catEyeSet/catEyeWorkMode/catEyeWorkMode_state.dart';
|
||||
import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart';
|
||||
import 'package:star_lock/network/api_repository.dart';
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'package:star_lock/tools/storage.dart';
|
||||
import 'package:star_lock/versionUndate/versionUndate_entity.dart';
|
||||
|
||||
class CatEyeWorkModeLogic extends BaseGetXController {
|
||||
@ -11,7 +16,8 @@ class CatEyeWorkModeLogic extends BaseGetXController {
|
||||
|
||||
//设置猫眼工作模式
|
||||
Future<void> updateCatEyeModeConfig() async {
|
||||
final VersionUndateEntity entity = await ApiRepository.to.updateCatEyeModeConfig(
|
||||
final VersionUndateEntity entity =
|
||||
await ApiRepository.to.updateCatEyeModeConfig(
|
||||
lockId: state.lockSetInfoData.value.lockId!,
|
||||
catEyeConfig: [
|
||||
<String, Object>{
|
||||
@ -53,9 +59,47 @@ class CatEyeWorkModeLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理蓝牙数据
|
||||
void _handlerBleData() {
|
||||
BlueManage().blueSendData(BlueManage().connectDeviceName,
|
||||
(BluetoothConnectionState connectionState) async {
|
||||
if (connectionState == BluetoothConnectionState.connected) {
|
||||
final List<String>? privateKey =
|
||||
await Storage.getStringList(saveBluePrivateKey);
|
||||
final List<int> getPrivateKeyList =
|
||||
changeStringListToIntList(privateKey!);
|
||||
|
||||
final List<String>? token = await Storage.getStringList(saveBlueToken);
|
||||
final List<int> getTokenList = changeStringListToIntList(token!);
|
||||
|
||||
final List<String>? publicKey =
|
||||
await Storage.getStringList(saveBluePublicKey);
|
||||
final List<int> getPublicKeyList =
|
||||
changeStringListToIntList(publicKey!);
|
||||
|
||||
List<int> featureData = [];
|
||||
|
||||
IoSenderManage.setSupportFunctionsWithParametersCommand(
|
||||
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
|
||||
userID: await Storage.getUid(),
|
||||
featureBit: 33,
|
||||
featureParaLength: 1,
|
||||
featureData: featureData,
|
||||
token: getTokenList,
|
||||
needAuthor: 1,
|
||||
publicKey: getPublicKeyList,
|
||||
privateKey: getPrivateKeyList);
|
||||
} else if (connectionState == BluetoothConnectionState.disconnected) {
|
||||
dismissEasyLoading();
|
||||
cancelBlueConnetctToastTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取锁设置信息
|
||||
Future<void> getLockSettingInfoData() async {
|
||||
final LockSetInfoEntity entity = await ApiRepository.to.getLockSettingInfoData(
|
||||
final LockSetInfoEntity entity =
|
||||
await ApiRepository.to.getLockSettingInfoData(
|
||||
lockId: state.lockSetInfoData.value.lockId.toString(),
|
||||
);
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
|
||||
@ -105,10 +105,11 @@ class ConfiguringWifiLogic extends BaseGetXController {
|
||||
serversList.add(type2);
|
||||
}
|
||||
|
||||
final StarChartRegisterNodeEntity? registerNodeEntity =
|
||||
await Storage.getStarChartRegisterNodeInfo();
|
||||
// 判断是否登录账户
|
||||
final loginData = await Storage.getLoginData();
|
||||
|
||||
// 获取app用户的peerId
|
||||
String appPeerId = registerNodeEntity?.peer?.id ?? '';
|
||||
String appPeerId = loginData?.starchart?.starchartId ?? '';
|
||||
|
||||
final List<String> uidList = <String>[Storage.getUid().toString()];
|
||||
IoSenderManage.senderConfiguringWifiCommand(
|
||||
@ -201,10 +202,11 @@ class ConfiguringWifiLogic extends BaseGetXController {
|
||||
final String? uidStr = await Storage.getUid();
|
||||
final List<String> uidList = <String>[uidStr.toString()];
|
||||
|
||||
final StarChartRegisterNodeEntity? registerNodeEntity =
|
||||
await Storage.getStarChartRegisterNodeInfo();
|
||||
// 判断是否登录账户
|
||||
final loginData = await Storage.getLoginData();
|
||||
|
||||
// 获取app用户的peerId
|
||||
String appPeerId = registerNodeEntity?.peer?.id ?? '';
|
||||
String appPeerId = loginData?.starchart?.starchartId ?? '';
|
||||
IoSenderManage.senderConfiguringWifiCommand(
|
||||
keyID: state.lockSetInfoData.value.lockBasicInfo!.keyId.toString(),
|
||||
userID: await Storage.getUid(),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -6,6 +6,7 @@ import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/udp_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/ble_message.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
|
||||
@ -386,7 +387,7 @@ class MessageCommand {
|
||||
final payload = rbcuInfo.writeToBuffer();
|
||||
ScpMessage message = ScpMessage(
|
||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||
MessageType: MessageTypeConstant.Resp,
|
||||
MessageType: MessageTypeConstant.Req,
|
||||
MessageId: MessageId,
|
||||
SpTotal: 1,
|
||||
SpIndex: 1,
|
||||
@ -401,6 +402,40 @@ class MessageCommand {
|
||||
return _hexToBytes(serializedBytesString);
|
||||
}
|
||||
|
||||
// 蓝牙消息
|
||||
static List<int> bleMessage({
|
||||
required String FromPeerId,
|
||||
required String ToPeerId,
|
||||
required String bluetoothDeviceName,
|
||||
required List<int> bleStructData,
|
||||
int? MessageId,
|
||||
int? timeout = 30,
|
||||
int? IdleTimeout = 0,
|
||||
}) {
|
||||
final bleReq = BleReq(
|
||||
bluetoothDeviceName: bluetoothDeviceName,
|
||||
structData: bleStructData,
|
||||
timeout: timeout,
|
||||
idleTimeout: 0,
|
||||
);
|
||||
final payload = bleReq.writeToBuffer();
|
||||
ScpMessage message = ScpMessage(
|
||||
ProtocolFlag: ProtocolFlagConstant.scp01,
|
||||
MessageType: MessageTypeConstant.Req,
|
||||
MessageId: MessageId,
|
||||
SpTotal: 1,
|
||||
SpIndex: 1,
|
||||
FromPeerId: FromPeerId,
|
||||
ToPeerId: ToPeerId,
|
||||
Payload: payload,
|
||||
PayloadCRC: calculationCrc(Uint8List.fromList(payload)),
|
||||
PayloadLength: payload.length,
|
||||
PayloadType: PayloadTypeConstant.blePassthrough,
|
||||
);
|
||||
String serializedBytesString = message.serialize();
|
||||
return _hexToBytes(serializedBytesString);
|
||||
}
|
||||
|
||||
// 辅助方法:将16进制字符串转换为字节列表
|
||||
static List<int> _hexToBytes(String hex) {
|
||||
final bytes = <int>[];
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_heart_beat_handler.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart';
|
||||
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/appRouters.dart';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/entity/scp_message.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart';
|
||||
@ -16,13 +19,16 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
|
||||
implements ScpMessageHandler {
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
final BleResp bleResp = scpMessage.Payload;
|
||||
//TODO 收到蓝牙透传请求指令
|
||||
}
|
||||
|
||||
@override
|
||||
void handleResp(ScpMessage scpMessage) {
|
||||
// TODO: 收到蓝牙透传指令回复
|
||||
final BleResp bleResp = scpMessage.Payload;
|
||||
// 如果回复成功
|
||||
// if (bleResp.status == BleResp_StatusE.SUCCESS) {
|
||||
AppLog.log('收到蓝牙消息回复:${bleResp.structData}');
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
@ -41,8 +47,17 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle
|
||||
int? spTotal,
|
||||
int? spIndex,
|
||||
int? messageId}) {
|
||||
final BleResp bleResp = BleResp();
|
||||
bleResp.mergeFromBuffer(byte);
|
||||
return bleResp;
|
||||
if (messageType == MessageTypeConstant.Resp) {
|
||||
final BleResp bleResp = BleResp();
|
||||
bleResp.mergeFromBuffer(byte);
|
||||
return bleResp;
|
||||
} else if (messageType == MessageTypeConstant.Req) {
|
||||
final BleReq talkExpect = BleReq();
|
||||
talkExpect.mergeFromBuffer(byte);
|
||||
return talkExpect;
|
||||
} else {
|
||||
String payload = utf8.decode(byte);
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,16 +46,20 @@ class UdpRbcuInfoHandler extends ScpMessageBaseHandle
|
||||
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
final RbcuInfo rbcuInfo = scpMessage.Payload();
|
||||
final RbcuInfo rbcuInfo = scpMessage.Payload;
|
||||
if (rbcuInfo.isResp) {
|
||||
// 如果是回复的消息
|
||||
_handleResultRbcuInfo(rbcuInfo);
|
||||
} else {
|
||||
// 回复
|
||||
startChartManage.replyRbcuInfoMessage(ToPeerId: scpMessage.FromPeerId!);
|
||||
}
|
||||
replySuccessMessage(scpMessage);
|
||||
}
|
||||
|
||||
@override
|
||||
void handleResp(ScpMessage scpMessage) {
|
||||
final GenericResp genericResp = scpMessage.Payload();
|
||||
final GenericResp genericResp = scpMessage.Payload;
|
||||
if (checkGenericRespSuccess(genericResp)) {
|
||||
// 收到回复之后停止重发
|
||||
startChartManage.stopSendingRbcuInfoMessages();
|
||||
@ -65,5 +69,6 @@ class UdpRbcuInfoHandler extends ScpMessageBaseHandle
|
||||
/// 处理回复的rbcuInfo消息
|
||||
void _handleResultRbcuInfo(RbcuInfo rbcuInfo) {
|
||||
P2pManage().communicationObjectRbcuInfo = rbcuInfo;
|
||||
startChartManage.stopSendingRbcuInfoMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,25 +12,21 @@ import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/views/talkView/talk_view_logic.dart';
|
||||
import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart';
|
||||
|
||||
import '../../start_chart_manage.dart';
|
||||
|
||||
class UdpTalkExpectHandler extends ScpMessageBaseHandle
|
||||
implements ScpMessageHandler {
|
||||
final TalkViewState talkViewState = TalkViewState();
|
||||
final TalkViewState talkViewState = Get.put(TalkViewLogic()).state;
|
||||
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
// 收到预期音视频数据请求
|
||||
final TalkExpectReq talkExpect = scpMessage.Payload;
|
||||
print('收到预期音视频数据请求:$talkExpect');
|
||||
|
||||
// 回复请求
|
||||
replySuccessMessage(scpMessage);
|
||||
|
||||
// 启动发送通话数据
|
||||
// startChartManage.startTalkDataTimer();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -38,10 +34,13 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle
|
||||
// 收到预期音视频数据回复
|
||||
final TalkExpectResp talkExpectResp = scpMessage.Payload;
|
||||
if (talkExpectResp != null) {
|
||||
print('收到预期音视频数据回复');
|
||||
// print('收到预期音视频数据回复,scpMessage:$scpMessage');
|
||||
// 停止发送预期数据的定时器
|
||||
startChartManage.stopTalkExpectMessageTimer();
|
||||
talkViewState.rotateAngle.value = talkExpectResp.rotate ?? 0;
|
||||
// 收到预期数据的应答后,代表建立了连接,启动通话保持的监听
|
||||
// 启动通话保持监听定时器(用来判断如果x秒内没有收到通话保持则执行的操作);
|
||||
talkePingOverTimeTimerManager.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,17 +15,17 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
|
||||
implements ScpMessageHandler {
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
if (talkStatus.status != TalkStatus.answeredSuccessfully) {
|
||||
// 如果不是接听中,不处理通话中挂断请求
|
||||
return;
|
||||
}
|
||||
// if (talkStatus.status != TalkStatus.answeredSuccessfully) {
|
||||
// // 如果不是接听中,不处理通话中挂断请求
|
||||
// return;
|
||||
// }
|
||||
print('收到通话中挂断请求');
|
||||
// 回复请求
|
||||
replySuccessMessage(scpMessage);
|
||||
talkStatus.setHangingUpDuring();
|
||||
stopRingtone();
|
||||
StartChartManage().stopTalkExpectMessageTimer();
|
||||
StartChartManage().stopTalkPingMessageTimer();
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -15,15 +15,13 @@ class UdpTalkPingHandler extends ScpMessageBaseHandle
|
||||
void handleReq(ScpMessage scpMessage) {
|
||||
// 收到通话保持请求,回复成功消息
|
||||
replySuccessMessage(scpMessage);
|
||||
talkePingOverTimeTimerManager.renew();
|
||||
}
|
||||
|
||||
@override
|
||||
void handleResp(ScpMessage scpMessage) {
|
||||
// 收到通话保持回复
|
||||
// print('收到通话保持回复');
|
||||
final GenericResp genericResp = scpMessage.Payload;
|
||||
if (checkGenericRespSuccess(genericResp)) {
|
||||
// 收到消息,续签定时器
|
||||
talkePingOverTimeTimerManager.renew();
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
implements ScpMessageHandler {
|
||||
@override
|
||||
void handleReq(ScpMessage scpMessage) async {
|
||||
// 回复成功
|
||||
replySuccessMessage(scpMessage);
|
||||
// 判断是否登录账户
|
||||
final loginData = await Storage.getLoginData();
|
||||
if (loginData == null ||
|
||||
@ -28,11 +30,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
// 如果已经是等待接听了,就不在处理剩下的请求
|
||||
return;
|
||||
}
|
||||
// 回复成功
|
||||
replySuccessMessage(scpMessage);
|
||||
// 收到对讲请求
|
||||
final TalkReq talkReq = scpMessage.Payload;
|
||||
|
||||
startChartManage.FromPeerId = scpMessage.ToPeerId!;
|
||||
startChartManage.ToPeerId = scpMessage.FromPeerId!;
|
||||
// 处理收到接听请求后的事件
|
||||
@ -64,8 +63,6 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
_showTalkRequestNotification(talkObjectName: talkObjectName);
|
||||
// 设置为等待接听状态
|
||||
talkStatus.setWaitingAnswer();
|
||||
// 启动通话保持
|
||||
_handleStartTalkPing();
|
||||
// 启动对讲请求超时定时器
|
||||
talkeRequestOverTimeTimerManager.start();
|
||||
// 收到呼叫请求,跳转到接听页面
|
||||
@ -123,12 +120,4 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
|
||||
// 修改预期数据并启动发送预期数据定时器,在收到回复时停止
|
||||
startChartManage.sendOnlyImageVideoTalkExpectData();
|
||||
}
|
||||
|
||||
// 启动通话保持,判断x秒内是否收到通话保持
|
||||
void _handleStartTalkPing() {
|
||||
// 启动通话保持
|
||||
startChartManage.startTalkPingMessageTimer();
|
||||
// 启动通话保持监听定时器(用来判断如果x秒内没有收到通话保持则执行的操作)
|
||||
talkePingOverTimeTimerManager.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,8 @@ import 'package:get/get.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
|
||||
|
||||
class TalkDataOverTimeTimerManager {
|
||||
// 单例实例
|
||||
|
||||
@ -5,7 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
|
||||
class TalkePingOverTimeTimerManager {
|
||||
// 单例实例
|
||||
@ -41,6 +41,8 @@ class TalkePingOverTimeTimerManager {
|
||||
|
||||
// 启动定时器
|
||||
void start() {
|
||||
// 取消之前的定时器
|
||||
_timer?.cancel();
|
||||
_timer = Timer(timeout, onTimeout);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
|
||||
class TalkeRequestOverTimeTimerManager {
|
||||
// 单例实例
|
||||
@ -31,6 +31,7 @@ class TalkeRequestOverTimeTimerManager {
|
||||
EasyLoading.showToast('通话未接通,以挂断', duration: 2000.milliseconds);
|
||||
// 超时未接听,发送挂断请求
|
||||
StartChartManage().sendTalkRejectMessage();
|
||||
talkStatus.setInitializationCompleted();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,8 @@ import 'package:star_lock/talk/startChart/handle/other/talke_request_over_time_t
|
||||
import 'package:star_lock/talk/startChart/proto/generic.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
|
||||
class ScpMessageBaseHandle {
|
||||
/// 使用单例 TimerManager
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_rbcuInfo_handler.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_talk_request_handler.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_echo_test_handler.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/impl/udp_gateway_reset_handler.dart';
|
||||
@ -55,7 +56,7 @@ class ScpMessageHandlerFactory {
|
||||
case PayloadTypeConstant.talkHangup:
|
||||
return UdpTalkHangUpHandler();
|
||||
case PayloadTypeConstant.RbcuInfo:
|
||||
return UdpTalkHangUpHandler();
|
||||
return UdpRbcuInfoHandler();
|
||||
default:
|
||||
return UnKnowPayloadTypeHandler();
|
||||
}
|
||||
|
||||
@ -1,14 +1,25 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
|
||||
|
||||
class P2pManage {
|
||||
// 常量定义
|
||||
static const String testMessage = 'Hello'; // 测试消息
|
||||
static const int localPort = 0; // 绑定本地端口(0 表示由系统分配)
|
||||
static const Duration connectionTimeout = Duration(seconds: 5); // 连接超时时间
|
||||
static const List<String> localNetworkPrefixes = [
|
||||
'192.168.',
|
||||
'10.',
|
||||
'172.'
|
||||
]; // 局域网 IP 前缀
|
||||
|
||||
RbcuInfo? communicationObjectRbcuInfo;
|
||||
|
||||
|
||||
void init(){
|
||||
|
||||
void init() {
|
||||
// 初始化逻辑
|
||||
}
|
||||
|
||||
|
||||
// 解析 address 属性,提取对方的 IP 和端口
|
||||
List<Map<String, String>> parseRemoteAddresses() {
|
||||
final addresses = communicationObjectRbcuInfo?.address ?? [];
|
||||
@ -18,4 +29,80 @@ class P2pManage {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// 判断是否为局域网 IP
|
||||
bool _isLocalNetworkIp(String ip) {
|
||||
for (final prefix in localNetworkPrefixes) {
|
||||
if (ip.startsWith(prefix)) {
|
||||
// 处理 172.16.x.x 到 172.31.x.x
|
||||
if (prefix == '172.') {
|
||||
final secondOctet = int.tryParse(ip.split('.')[1]) ?? 0;
|
||||
if (secondOctet >= 16 && secondOctet <= 31) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建 UDP 连接
|
||||
Future<void> createUdpConnection() async {
|
||||
final addresses = parseRemoteAddresses();
|
||||
|
||||
// 优先使用局域网 IP
|
||||
addresses.sort((a, b) {
|
||||
final isALocal = _isLocalNetworkIp(a['ip']!);
|
||||
final isBLocal = _isLocalNetworkIp(b['ip']!);
|
||||
if (isALocal && !isBLocal) return -1; // a 是局域网 IP,优先级更高
|
||||
if (!isALocal && isBLocal) return 1; // b 是局域网 IP,优先级更高
|
||||
return 0; // 优先级相同
|
||||
});
|
||||
|
||||
// 尝试连接
|
||||
for (final addr in addresses) {
|
||||
final ip = addr['ip']!;
|
||||
final port = int.parse(addr['port']!);
|
||||
|
||||
try {
|
||||
final socket = await RawDatagramSocket.bind(
|
||||
InternetAddress.anyIPv4, localPort); // 绑定本地端口
|
||||
|
||||
// 发送测试数据
|
||||
final testData = testMessage.codeUnits;
|
||||
socket.send(testData, InternetAddress(ip), port);
|
||||
|
||||
// 设置超时时间
|
||||
final completer = Completer<void>();
|
||||
|
||||
// 监听响应
|
||||
socket.listen((event) {
|
||||
if (event == RawSocketEvent.read) {
|
||||
final datagram = socket.receive();
|
||||
if (datagram != null) {
|
||||
final response = String.fromCharCodes(datagram.data);
|
||||
print('收到来自 $ip:$port 的响应: $response');
|
||||
completer.complete(); // 收到响应,完成 Future
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 等待响应或超时
|
||||
try {
|
||||
await completer.future.timeout(connectionTimeout);
|
||||
print('成功连接到 $ip:$port');
|
||||
return; // 连接成功,退出循环
|
||||
} on TimeoutException {
|
||||
print('连接到 $ip:$port 超时');
|
||||
} finally {
|
||||
socket.close(); // 关闭 socket
|
||||
}
|
||||
} catch (e) {
|
||||
print('连接到 $ip:$port 失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
print('无法连接到任何地址');
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,18 +17,27 @@ import 'ble_message.pbenum.dart';
|
||||
|
||||
export 'ble_message.pbenum.dart';
|
||||
|
||||
/// 用于蓝牙透传的数据结构
|
||||
class BleReq extends $pb.GeneratedMessage {
|
||||
factory BleReq({
|
||||
$core.int? timeout,
|
||||
$core.String? bluetoothDeviceName,
|
||||
$core.List<$core.int>? structData,
|
||||
$core.int? idleTimeout,
|
||||
}) {
|
||||
final $result = create();
|
||||
if (timeout != null) {
|
||||
$result.timeout = timeout;
|
||||
}
|
||||
if (bluetoothDeviceName != null) {
|
||||
$result.bluetoothDeviceName = bluetoothDeviceName;
|
||||
}
|
||||
if (structData != null) {
|
||||
$result.structData = structData;
|
||||
}
|
||||
if (idleTimeout != null) {
|
||||
$result.idleTimeout = idleTimeout;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
BleReq._() : super();
|
||||
@ -36,8 +45,10 @@ class BleReq extends $pb.GeneratedMessage {
|
||||
factory BleReq.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BleReq', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create)
|
||||
..a<$core.int>(1, _omitFieldNames ? '' : 'Timeout', $pb.PbFieldType.OU3, protoName: 'Timeout')
|
||||
..aOS(2, _omitFieldNames ? '' : 'bluetoothDeviceName', protoName: 'bluetoothDeviceName')
|
||||
..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'StructData', $pb.PbFieldType.OY, protoName: 'StructData')
|
||||
..a<$core.int>(6, _omitFieldNames ? '' : 'IdleTimeout', $pb.PbFieldType.OU3, protoName: 'IdleTimeout')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@ -62,25 +73,45 @@ class BleReq extends $pb.GeneratedMessage {
|
||||
static BleReq getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BleReq>(create);
|
||||
static BleReq? _defaultInstance;
|
||||
|
||||
/// 扫描+发送+接受的总超时时间,单位为秒
|
||||
@$pb.TagNumber(1)
|
||||
$core.int get timeout => $_getIZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set timeout($core.int v) { $_setUnsignedInt32(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasTimeout() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
void clearTimeout() => clearField(1);
|
||||
|
||||
/// 用唯一的设备名来进行匹配而不是mac地址
|
||||
@$pb.TagNumber(2)
|
||||
$core.String get bluetoothDeviceName => $_getSZ(0);
|
||||
$core.String get bluetoothDeviceName => $_getSZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set bluetoothDeviceName($core.String v) { $_setString(0, v); }
|
||||
set bluetoothDeviceName($core.String v) { $_setString(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasBluetoothDeviceName() => $_has(0);
|
||||
$core.bool hasBluetoothDeviceName() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearBluetoothDeviceName() => clearField(2);
|
||||
|
||||
/// 下面是蓝牙结构内容,未加密状态
|
||||
@$pb.TagNumber(3)
|
||||
$core.List<$core.int> get structData => $_getN(1);
|
||||
$core.List<$core.int> get structData => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set structData($core.List<$core.int> v) { $_setBytes(1, v); }
|
||||
set structData($core.List<$core.int> v) { $_setBytes(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasStructData() => $_has(1);
|
||||
$core.bool hasStructData() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearStructData() => clearField(3);
|
||||
|
||||
/// 空闲断开时间(秒),0为立即断开
|
||||
@$pb.TagNumber(6)
|
||||
$core.int get idleTimeout => $_getIZ(3);
|
||||
@$pb.TagNumber(6)
|
||||
set idleTimeout($core.int v) { $_setUnsignedInt32(3, v); }
|
||||
@$pb.TagNumber(6)
|
||||
$core.bool hasIdleTimeout() => $_has(3);
|
||||
@$pb.TagNumber(6)
|
||||
void clearIdleTimeout() => clearField(6);
|
||||
}
|
||||
|
||||
class BleResp extends $pb.GeneratedMessage {
|
||||
@ -103,7 +134,7 @@ class BleResp extends $pb.GeneratedMessage {
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BleResp', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create)
|
||||
..e<BleResp_StatusE>(1, _omitFieldNames ? '' : 'Status', $pb.PbFieldType.OE, protoName: 'Status', defaultOrMaker: BleResp_StatusE.SUCCESS, valueOf: BleResp_StatusE.valueOf, enumValues: BleResp_StatusE.values)
|
||||
..a<$core.List<$core.int>>(4, _omitFieldNames ? '' : 'StructData', $pb.PbFieldType.OY, protoName: 'StructData')
|
||||
..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'StructData', $pb.PbFieldType.OY, protoName: 'StructData')
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@ -138,15 +169,15 @@ class BleResp extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(1)
|
||||
void clearStatus() => clearField(1);
|
||||
|
||||
/// 下面是蓝牙结构内容,未加密状态
|
||||
@$pb.TagNumber(4)
|
||||
/// 下面是蓝牙结构内容
|
||||
@$pb.TagNumber(2)
|
||||
$core.List<$core.int> get structData => $_getN(1);
|
||||
@$pb.TagNumber(4)
|
||||
@$pb.TagNumber(2)
|
||||
set structData($core.List<$core.int> v) { $_setBytes(1, v); }
|
||||
@$pb.TagNumber(4)
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasStructData() => $_has(1);
|
||||
@$pb.TagNumber(4)
|
||||
void clearStructData() => clearField(4);
|
||||
@$pb.TagNumber(2)
|
||||
void clearStructData() => clearField(2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -18,16 +18,16 @@ class BleResp_StatusE extends $pb.ProtobufEnum {
|
||||
static const BleResp_StatusE SUCCESS = BleResp_StatusE._(0, _omitEnumNames ? '' : 'SUCCESS');
|
||||
static const BleResp_StatusE FAIL = BleResp_StatusE._(1, _omitEnumNames ? '' : 'FAIL');
|
||||
static const BleResp_StatusE NOT_FOUND = BleResp_StatusE._(2, _omitEnumNames ? '' : 'NOT_FOUND');
|
||||
static const BleResp_StatusE CANNOT_CONNECT = BleResp_StatusE._(3, _omitEnumNames ? '' : 'CANNOT_CONNECT');
|
||||
static const BleResp_StatusE CANNOT_SEND = BleResp_StatusE._(4, _omitEnumNames ? '' : 'CANNOT_SEND');
|
||||
static const BleResp_StatusE CONNECT_FAIL = BleResp_StatusE._(3, _omitEnumNames ? '' : 'CONNECT_FAIL');
|
||||
static const BleResp_StatusE SEND_FAIL = BleResp_StatusE._(4, _omitEnumNames ? '' : 'SEND_FAIL');
|
||||
static const BleResp_StatusE TIMEOUT = BleResp_StatusE._(5, _omitEnumNames ? '' : 'TIMEOUT');
|
||||
|
||||
static const $core.List<BleResp_StatusE> values = <BleResp_StatusE> [
|
||||
SUCCESS,
|
||||
FAIL,
|
||||
NOT_FOUND,
|
||||
CANNOT_CONNECT,
|
||||
CANNOT_SEND,
|
||||
CONNECT_FAIL,
|
||||
SEND_FAIL,
|
||||
TIMEOUT,
|
||||
];
|
||||
|
||||
|
||||
@ -19,20 +19,23 @@ const BleReq$json = {
|
||||
'2': [
|
||||
{'1': 'bluetoothDeviceName', '3': 2, '4': 1, '5': 9, '10': 'bluetoothDeviceName'},
|
||||
{'1': 'StructData', '3': 3, '4': 1, '5': 12, '10': 'StructData'},
|
||||
{'1': 'Timeout', '3': 1, '4': 1, '5': 13, '10': 'Timeout'},
|
||||
{'1': 'IdleTimeout', '3': 6, '4': 1, '5': 13, '10': 'IdleTimeout'},
|
||||
],
|
||||
};
|
||||
|
||||
/// Descriptor for `BleReq`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List bleReqDescriptor = $convert.base64Decode(
|
||||
'CgZCbGVSZXESMAoTYmx1ZXRvb3RoRGV2aWNlTmFtZRgCIAEoCVITYmx1ZXRvb3RoRGV2aWNlTm'
|
||||
'FtZRIeCgpTdHJ1Y3REYXRhGAMgASgMUgpTdHJ1Y3REYXRh');
|
||||
'FtZRIeCgpTdHJ1Y3REYXRhGAMgASgMUgpTdHJ1Y3REYXRhEhgKB1RpbWVvdXQYASABKA1SB1Rp'
|
||||
'bWVvdXQSIAoLSWRsZVRpbWVvdXQYBiABKA1SC0lkbGVUaW1lb3V0');
|
||||
|
||||
@$core.Deprecated('Use bleRespDescriptor instead')
|
||||
const BleResp$json = {
|
||||
'1': 'BleResp',
|
||||
'2': [
|
||||
{'1': 'Status', '3': 1, '4': 1, '5': 14, '6': '.main.BleResp.StatusE', '10': 'Status'},
|
||||
{'1': 'StructData', '3': 4, '4': 1, '5': 12, '10': 'StructData'},
|
||||
{'1': 'StructData', '3': 2, '4': 1, '5': 12, '10': 'StructData'},
|
||||
],
|
||||
'4': [BleResp_StatusE$json],
|
||||
};
|
||||
@ -44,8 +47,8 @@ const BleResp_StatusE$json = {
|
||||
{'1': 'SUCCESS', '2': 0},
|
||||
{'1': 'FAIL', '2': 1},
|
||||
{'1': 'NOT_FOUND', '2': 2},
|
||||
{'1': 'CANNOT_CONNECT', '2': 3},
|
||||
{'1': 'CANNOT_SEND', '2': 4},
|
||||
{'1': 'CONNECT_FAIL', '2': 3},
|
||||
{'1': 'SEND_FAIL', '2': 4},
|
||||
{'1': 'TIMEOUT', '2': 5},
|
||||
],
|
||||
};
|
||||
@ -53,7 +56,7 @@ const BleResp_StatusE$json = {
|
||||
/// Descriptor for `BleResp`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List bleRespDescriptor = $convert.base64Decode(
|
||||
'CgdCbGVSZXNwEi0KBlN0YXR1cxgBIAEoDjIVLm1haW4uQmxlUmVzcC5TdGF0dXNFUgZTdGF0dX'
|
||||
'MSHgoKU3RydWN0RGF0YRgEIAEoDFIKU3RydWN0RGF0YSJhCgdTdGF0dXNFEgsKB1NVQ0NFU1MQ'
|
||||
'ABIICgRGQUlMEAESDQoJTk9UX0ZPVU5EEAISEgoOQ0FOTk9UX0NPTk5FQ1QQAxIPCgtDQU5OT1'
|
||||
'RfU0VORBAEEgsKB1RJTUVPVVQQBQ==');
|
||||
'MSHgoKU3RydWN0RGF0YRgCIAEoDFIKU3RydWN0RGF0YSJdCgdTdGF0dXNFEgsKB1NVQ0NFU1MQ'
|
||||
'ABIICgRGQUlMEAESDQoJTk9UX0ZPVU5EEAISEAoMQ09OTkVDVF9GQUlMEAMSDQoJU0VORF9GQU'
|
||||
'lMEAQSCwoHVElNRU9VVBAF');
|
||||
|
||||
|
||||
@ -3,26 +3,29 @@ package main;
|
||||
option go_package = "./spb";
|
||||
|
||||
// 用于蓝牙透传的数据结构
|
||||
|
||||
message BleReq {
|
||||
// 用唯一的设备名来进行匹配而不是mac地址
|
||||
string bluetoothDeviceName = 2;
|
||||
// 下面是蓝牙结构内容,未加密状态
|
||||
bytes StructData = 3;
|
||||
// 扫描+发送+接受的总超时时间,单位为秒
|
||||
uint32 Timeout = 1;
|
||||
// 空闲断开时间(秒),0为立即断开
|
||||
uint32 IdleTimeout = 6;
|
||||
}
|
||||
|
||||
message BleResp {
|
||||
// 定义一个枚举状态【成功,失败,找不到设备,无法建立连接,无法发送数据,超时】
|
||||
enum StatusE {
|
||||
SUCCESS = 0;
|
||||
FAIL = 1;
|
||||
NOT_FOUND = 2;
|
||||
CANNOT_CONNECT = 3;
|
||||
CANNOT_SEND = 4;
|
||||
TIMEOUT = 5;
|
||||
SUCCESS = 0; // 成功
|
||||
FAIL = 1; // 其他失败(需要错误消息)
|
||||
NOT_FOUND = 2; // 找不到设备
|
||||
CONNECT_FAIL = 3; // 建立连接失败
|
||||
SEND_FAIL = 4; // 发送数据失败
|
||||
TIMEOUT = 5; // 超时
|
||||
}
|
||||
// 状态
|
||||
StatusE Status = 1;
|
||||
// 下面是蓝牙结构内容,未加密状态
|
||||
bytes StructData = 4;
|
||||
// 下面是蓝牙结构内容
|
||||
bytes StructData = 2;
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_expect.pbserver.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/tools/baseGetXController.dart';
|
||||
import 'package:star_lock/tools/deviceInfo_utils.dart';
|
||||
import 'package:star_lock/tools/storage.dart';
|
||||
@ -221,7 +221,8 @@ class StartChartManage {
|
||||
}
|
||||
|
||||
// 发送RbcuInfo 地址交换消息
|
||||
void _sendRbcuInfoMessage() async {
|
||||
void _sendRbcuInfoMessage(
|
||||
{required String ToPeerId, bool isResp = false}) async {
|
||||
final uuid = _uuid.v1();
|
||||
final int timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final Int64 int64Timestamp = Int64(timestamp); // 使用构造函数
|
||||
@ -252,7 +253,7 @@ class StartChartManage {
|
||||
name: uuid,
|
||||
address: address,
|
||||
time: int64Timestamp,
|
||||
isResp: false,
|
||||
isResp: isResp,
|
||||
);
|
||||
final message = MessageCommand.genericRbcuInfoMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
@ -264,12 +265,12 @@ class StartChartManage {
|
||||
}
|
||||
|
||||
// 启动定时任务
|
||||
void startSendingRbcuInfoMessages() {
|
||||
void startSendingRbcuInfoMessages({required String ToPeerId}) {
|
||||
// 每隔 1 秒执行一次 _sendRbcuInfoMessage
|
||||
rbcuInfoTimer ??=
|
||||
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
|
||||
// 发送RbcuInfo 地址交换消息
|
||||
_sendRbcuInfoMessage();
|
||||
_sendRbcuInfoMessage(ToPeerId: ToPeerId);
|
||||
});
|
||||
}
|
||||
|
||||
@ -279,6 +280,11 @@ class StartChartManage {
|
||||
rbcuInfoTimer = null;
|
||||
}
|
||||
|
||||
// 回复RbcuInfo
|
||||
void replyRbcuInfoMessage({required String ToPeerId}) {
|
||||
_sendRbcuInfoMessage(ToPeerId: ToPeerId, isResp: true);
|
||||
}
|
||||
|
||||
// 发送上线消息
|
||||
Future<void> _sendOnlineMessage() async {
|
||||
if (isOnlineStartChartServer) {
|
||||
@ -329,7 +335,7 @@ class StartChartManage {
|
||||
MessageCommand.getNextMessageId(ToPeerId, increment: false);
|
||||
// 组装分包数据
|
||||
final message = MessageCommand.talkDataMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
ToPeerId: 'D78Fo4CjNzUXz8DxuUhLtcRpnGFXhSzhzs191XzhJttS',
|
||||
FromPeerId: FromPeerId,
|
||||
payload: packet,
|
||||
SpTotal: totalPackets,
|
||||
@ -891,8 +897,36 @@ class StartChartManage {
|
||||
talkExpect: talkExpectReq);
|
||||
}
|
||||
|
||||
/// 发送远程开锁
|
||||
void sendRemoteUnLockMessage({
|
||||
required String bluetoothDeviceName,
|
||||
required List<int> openLockCommand,
|
||||
}) {
|
||||
sendBleMessage(
|
||||
bluetoothDeviceName: bluetoothDeviceName,
|
||||
bleStructData: openLockCommand,
|
||||
);
|
||||
}
|
||||
|
||||
/// 发送蓝牙透传消息
|
||||
void sendBleMessage({
|
||||
required String bluetoothDeviceName,
|
||||
required List<int> bleStructData,
|
||||
}) {
|
||||
// 组装上线消息
|
||||
final message = MessageCommand.bleMessage(
|
||||
FromPeerId: FromPeerId,
|
||||
ToPeerId: ToPeerId,
|
||||
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
|
||||
bluetoothDeviceName: bluetoothDeviceName,
|
||||
bleStructData: bleStructData,
|
||||
);
|
||||
_sendMessage(message: message);
|
||||
}
|
||||
|
||||
/// 销毁资源
|
||||
void destruction() async {
|
||||
sendTalkHangupMessage();
|
||||
isOnlineStartChartServer = false;
|
||||
stopHeartbeat();
|
||||
stopTalkExpectMessageTimer();
|
||||
|
||||
@ -25,7 +25,7 @@ class AppLifecycleObserver extends WidgetsBindingObserver {
|
||||
void onAppPaused() {
|
||||
// 处理应用程序进入后台的逻辑
|
||||
print('App has entered the background.');
|
||||
StartChartManage().destruction();
|
||||
// StartChartManage().destruction();
|
||||
}
|
||||
|
||||
void onAppResumed() {
|
||||
@ -16,6 +16,13 @@ import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:star_lock/app_settings/app_settings.dart';
|
||||
import 'package:star_lock/blue/blue_manage.dart';
|
||||
import 'package:star_lock/blue/io_protocol/io_openLock.dart';
|
||||
import 'package:star_lock/blue/io_tool/io_tool.dart';
|
||||
import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_logic.dart';
|
||||
import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_state.dart';
|
||||
import 'package:star_lock/main/lockDetail/lockDetail/lockNetToken_entity.dart';
|
||||
import 'package:star_lock/network/api_repository.dart';
|
||||
|
||||
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
@ -24,12 +31,16 @@ import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_manage.dart';
|
||||
|
||||
import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart';
|
||||
import 'package:star_lock/talk/udp/udp_manage.dart';
|
||||
import 'package:star_lock/talk/udp/udp_senderManage.dart';
|
||||
import 'package:star_lock/tools/bugly/bugly_tool.dart';
|
||||
import 'package:star_lock/tools/storage.dart';
|
||||
|
||||
import '../../../../tools/baseGetXController.dart';
|
||||
|
||||
class TalkViewLogic extends BaseGetXController {
|
||||
final TalkViewState state = TalkViewState();
|
||||
|
||||
final LockDetailState lockDetailState = Get.find<LockDetailLogic>().state;
|
||||
Timer? _syncTimer; // 音视频播放刷新率定时器
|
||||
int _startTime = 0; // 开始播放时间戳,用于判断帧数据中的时间戳位置
|
||||
final int bufferSize = 20; // 缓冲区大小(以帧为单位)
|
||||
@ -258,7 +269,61 @@ class TalkViewLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
/// 开门
|
||||
udpOpenDoorAction(List<int> list) async {}
|
||||
udpOpenDoorAction() async {
|
||||
final List<String>? privateKey =
|
||||
await Storage.getStringList(saveBluePrivateKey);
|
||||
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
|
||||
|
||||
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
|
||||
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
|
||||
|
||||
final List<String>? token = await Storage.getStringList(saveBlueToken);
|
||||
final List<int> getTokenList = changeStringListToIntList(token!);
|
||||
|
||||
await _getLockNetToken();
|
||||
|
||||
final OpenLockCommand openLockCommand = OpenLockCommand(
|
||||
lockID: BlueManage().connectDeviceName,
|
||||
userID: await Storage.getUid(),
|
||||
openMode: lockDetailState.openDoorModel,
|
||||
openTime: _getUTCNetTime(),
|
||||
onlineToken: lockDetailState.lockNetToken,
|
||||
token: getTokenList,
|
||||
needAuthor: 1,
|
||||
signKey: signKeyDataList,
|
||||
privateKey: getPrivateKeyList,
|
||||
);
|
||||
final messageDetail = openLockCommand.messageDetail();
|
||||
// 发送远程开门消息
|
||||
StartChartManage().sendRemoteUnLockMessage(
|
||||
bluetoothDeviceName: BlueManage().connectDeviceName,
|
||||
openLockCommand: messageDetail,
|
||||
);
|
||||
showToast('已发送开门通知');
|
||||
}
|
||||
|
||||
int _getUTCNetTime() {
|
||||
if (lockDetailState.isHaveNetwork) {
|
||||
return DateTime.now().millisecondsSinceEpoch ~/ 1000 +
|
||||
lockDetailState.differentialTime;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取手机联网token,根据锁设置里面获取的开锁时是否联网来判断是否调用这个接口
|
||||
Future<void> _getLockNetToken() async {
|
||||
final LockNetTokenEntity entity = await ApiRepository.to.getLockNetToken(
|
||||
lockId: lockDetailState.keyInfos.value.lockId.toString());
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
lockDetailState.lockNetToken = entity.data!.token!.toString();
|
||||
AppLog.log('从服务器获取联网token:${lockDetailState.lockNetToken}');
|
||||
} else {
|
||||
BuglyTool.uploadException(
|
||||
message: '点击了需要联网开锁', detail: '点击了需要联网开锁 获取连网token失败', upload: true);
|
||||
showToast('网络访问失败,请检查网络是否正常'.tr, something: () {});
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取权限状态
|
||||
Future<bool> getPermissionStatus() async {
|
||||
@ -285,6 +350,55 @@ class TalkViewLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> requestPermissions() async {
|
||||
// 申请存储权限
|
||||
var storageStatus = await Permission.storage.request();
|
||||
// 申请录音权限
|
||||
var microphoneStatus = await Permission.microphone.request();
|
||||
|
||||
if (storageStatus.isGranted && microphoneStatus.isGranted) {
|
||||
print("Permissions granted");
|
||||
} else {
|
||||
print("Permissions denied");
|
||||
// 如果权限被拒绝,可以提示用户或跳转到设置页面
|
||||
if (await Permission.storage.isPermanentlyDenied) {
|
||||
openAppSettings(); // 跳转到应用设置页面
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startRecording() async {
|
||||
requestPermissions();
|
||||
if (state.isRecordingScreen.value) {
|
||||
showToast('录屏已开始,请勿重复点击');
|
||||
}
|
||||
bool start = await FlutterScreenRecording.startRecordScreen(
|
||||
"Screen Recording", // 视频文件名
|
||||
titleNotification: "Recording in progress", // 通知栏标题
|
||||
messageNotification: "Tap to stop recording", // 通知栏内容
|
||||
);
|
||||
|
||||
if (start) {
|
||||
state.isRecordingScreen.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopRecording() async {
|
||||
String path = await FlutterScreenRecording.stopRecordScreen;
|
||||
print("Recording saved to: $path");
|
||||
|
||||
// 将视频保存到系统相册
|
||||
bool? success = await GallerySaver.saveVideo(path);
|
||||
if (success == true) {
|
||||
print("Video saved to gallery");
|
||||
} else {
|
||||
print("Failed to save video to gallery");
|
||||
}
|
||||
|
||||
showToast('录屏结束,已保存到系统相册');
|
||||
state.isRecordingScreen.value = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
@ -310,12 +424,16 @@ class TalkViewLogic extends BaseGetXController {
|
||||
|
||||
// 初始化录音控制器
|
||||
_initAudioRecorder();
|
||||
|
||||
requestPermissions();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_stopPlayG711Data();
|
||||
state.listData.value = Uint8List(0);
|
||||
state.audioBuffer.clear();
|
||||
state.videoBuffer.clear();
|
||||
_syncTimer?.cancel();
|
||||
_syncTimer = null;
|
||||
}
|
||||
@ -387,32 +505,6 @@ class TalkViewLogic extends BaseGetXController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 开始录屏
|
||||
Future<void> startRecording() async {
|
||||
getPermissionStatus();
|
||||
bool started =
|
||||
await FlutterScreenRecording.startRecordScreenAndAudio("Recording");
|
||||
if (started) {
|
||||
state.isRecordingScreen.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止录屏
|
||||
Future<void> stopRecording() async {
|
||||
String path = await FlutterScreenRecording.stopRecordScreen;
|
||||
if (path != null) {
|
||||
state.isRecordingScreen.value = false;
|
||||
// 保存录制的视频到相册
|
||||
// await GallerySaver.saveVideo(path).then((bool? success) {});
|
||||
// 将截图保存到相册
|
||||
await ImageGallerySaver.saveFile(path);
|
||||
showToast('录屏已保存到相册'.tr);
|
||||
} else {
|
||||
state.isRecordingScreen.value = false;
|
||||
print("Recording failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化音频录制器
|
||||
void _initAudioRecorder() {
|
||||
state.voiceProcessor = VoiceProcessor.instance;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -15,6 +17,7 @@ import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
|
||||
import 'package:star_lock/talk/startChart/views/talkView/talk_view_logic.dart';
|
||||
import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart';
|
||||
import 'package:star_lock/talk/udp/udp_manage.dart';
|
||||
|
||||
import '../../../../app_settings/app_colors.dart';
|
||||
import '../../../../tools/showTFView.dart';
|
||||
@ -35,6 +38,7 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
state.listData.value = Uint8List(0);
|
||||
//写一个定时器,三十秒后页面自动返回
|
||||
// state.autoBackTimer = Timer(const Duration(seconds: 30), Get.back);
|
||||
|
||||
@ -65,43 +69,61 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Obx(
|
||||
() => state.listData.value.isEmpty
|
||||
? Image.asset(
|
||||
'images/main/monitorBg.png',
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Container(
|
||||
decoration: state.isRecordingScreen.value
|
||||
? BoxDecoration(
|
||||
border: Border.all(color: Colors.red, width: 1))
|
||||
: BoxDecoration(),
|
||||
child: PopScope(
|
||||
() {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
|
||||
final logicalWidth = MediaQuery.of(context).size.width;
|
||||
final logicalHeight = MediaQuery.of(context).size.height;
|
||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||
|
||||
// 计算物理像素值
|
||||
final physicalWidth = logicalWidth * devicePixelRatio;
|
||||
final physicalHeight = logicalHeight * devicePixelRatio;
|
||||
|
||||
// 旋转后的图片尺寸
|
||||
final rotatedImageWidth = 480; // 原始高度
|
||||
final rotatedImageHeight = 864; // 原始宽度
|
||||
|
||||
// 计算缩放比例
|
||||
final scaleWidth = physicalWidth / rotatedImageWidth;
|
||||
final scaleHeight = physicalHeight / rotatedImageHeight;
|
||||
final scale = max(scaleWidth, scaleHeight); // 选择较大的缩放比例
|
||||
|
||||
return state.listData.value.isEmpty
|
||||
? Image.asset(
|
||||
'images/main/monitorBg.png',
|
||||
width: screenWidth,
|
||||
height: screenHeight,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: PopScope(
|
||||
canPop: false,
|
||||
child: RepaintBoundary(
|
||||
key: state.globalKey,
|
||||
child: RotatedBox(
|
||||
quarterTurns: 1, // 顺时针旋转 90 度(1 个四分之一圈)
|
||||
child: Image.memory(
|
||||
state.listData.value,
|
||||
gaplessPlayback: true,
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.high,
|
||||
errorBuilder: (
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
) {
|
||||
return Container(color: Colors.transparent);
|
||||
},
|
||||
child: Transform.rotate(
|
||||
angle:
|
||||
state.rotateAngle.value * (pi / 180), // 旋转 90 度
|
||||
child: Transform.scale(
|
||||
scale: scale, // 动态计算的缩放比例
|
||||
child: Image.memory(
|
||||
state.listData.value,
|
||||
gaplessPlayback: true,
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.high,
|
||||
errorBuilder: (
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
) {
|
||||
return Container(color: Colors.transparent);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Obx(() => state.listData.value.isEmpty
|
||||
? Positioned(
|
||||
@ -200,13 +222,12 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
logic.showToast('功能暂未开放'.tr);
|
||||
// if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
|
||||
// if (
|
||||
// state.talkStatus.value == TalkStatus.answeredSuccessfully) {
|
||||
// if (state.isRecordingScreen.value) {
|
||||
// await logic.stopRecording();
|
||||
// print('停止录屏');
|
||||
// } else {
|
||||
// await logic.startRecording();
|
||||
// print('开始录屏');
|
||||
// }
|
||||
// }
|
||||
},
|
||||
@ -276,7 +297,14 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
'images/main/icon_lockDetail_monitoringUnlock.png',
|
||||
'开锁',
|
||||
AppColors.mainColor,
|
||||
onClick: () {},
|
||||
onClick: () {
|
||||
if (UDPManage().remoteUnlock == 1) {
|
||||
logic.udpOpenDoorAction();
|
||||
// showDeletPasswordAlertDialog(context);
|
||||
} else {
|
||||
logic.showToast('请在锁设置中开启远程开锁'.tr);
|
||||
}
|
||||
},
|
||||
)
|
||||
]);
|
||||
}
|
||||
@ -342,7 +370,7 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ShowTFView(
|
||||
title: '请输入六位数字开锁密码'.tr,
|
||||
title: '请输入6位数字开锁密码'.tr,
|
||||
tipTitle: '',
|
||||
controller: state.passwordTF,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
@ -350,22 +378,23 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
FilteringTextInputFormatter.allow(RegExp('[0-9]')),
|
||||
],
|
||||
sureClick: () async {
|
||||
// //发送删除锁请求
|
||||
//发送删除锁请求
|
||||
// if (state.passwordTF.text.isEmpty) {
|
||||
// logic.showToast('请输入开锁密码'.tr);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // List<int> numbers = state.passwordTF.text.split('').map((char) => int.parse(char)).toList();
|
||||
// // 开锁
|
||||
// // lockID
|
||||
// List<int> numbers = <int>[];
|
||||
// List<int> lockIDData = utf8.encode(state.passwordTF.text);
|
||||
|
||||
// List<int> numbers = state.passwordTF.text.split('').map((char) => int.parse(char)).toList();
|
||||
// 开锁
|
||||
// lockID
|
||||
// final List<int> numbers = <int>[];
|
||||
// final List<int> lockIDData = utf8.encode(state.passwordTF.text);
|
||||
// numbers.addAll(lockIDData);
|
||||
// // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length);
|
||||
// for (int i = 0; i < 6 - lockIDData.length; i++) {
|
||||
// numbers.add(0);
|
||||
// }
|
||||
logic.udpOpenDoorAction();
|
||||
},
|
||||
cancelClick: () {
|
||||
Get.back();
|
||||
@ -409,6 +438,8 @@ class _TalkViewPageState extends State<TalkViewPage>
|
||||
state.animationController.dispose();
|
||||
state.realTimePicTimer.cancel();
|
||||
state.autoBackTimer.cancel();
|
||||
state.videoBuffer.clear();
|
||||
state.listData.value = Uint8List(0);
|
||||
CallTalk().finishAVData();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import 'package:network_info_plus/network_info_plus.dart';
|
||||
import 'package:star_lock/talk/startChart/constant/talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart';
|
||||
import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart';
|
||||
import 'package:star_lock/talk/startChart/start_chart_talk_status.dart';
|
||||
import 'package:star_lock/talk/startChart/status/start_chart_talk_status.dart';
|
||||
|
||||
import '../../../../tools/storage.dart';
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user