app-starlock/lib/talk/starChart/star_chart_manage.dart

1238 lines
41 KiB
Dart
Raw Normal View History

2024-11-28 14:57:49 +08:00
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:star_lock/appRouters.dart';
2024-11-28 14:57:49 +08:00
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/flavors.dart';
import 'package:star_lock/login/login/entity/LoginData.dart';
import 'package:star_lock/login/login/entity/LoginEntity.dart';
2025-02-18 14:03:44 +08:00
import 'package:star_lock/main/lockDetail/lockDetail/device_network_info.dart';
import 'package:star_lock/main/lockMian/entity/lockListInfo_entity.dart';
import 'package:star_lock/network/api_repository.dart';
2024-11-28 14:57:49 +08:00
import 'package:star_lock/network/start_chart_api.dart';
import 'package:star_lock/talk/other/audio_player_manager.dart';
2025-01-23 14:30:31 +08:00
import 'package:star_lock/talk/starChart/command/message_command.dart';
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';
2025-04-18 10:33:51 +08:00
import 'package:star_lock/talk/starChart/constant/talk_constant.dart';
2025-01-23 14:30:31 +08:00
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';
import 'package:star_lock/talk/starChart/entity/scp_message.dart';
import 'package:star_lock/talk/starChart/entity/star_chart_register_node_entity.dart';
import 'package:star_lock/talk/starChart/exception/start_chart_message_exception.dart';
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';
2025-04-18 10:33:51 +08:00
import 'package:star_lock/talk/starChart/handle/other/packet_loss_statistics.dart';
2025-01-23 14:30:31 +08:00
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';
import 'package:star_lock/talk/starChart/handle/scp_message_handle.dart';
import 'package:star_lock/talk/starChart/handle/scp_message_handler_factory.dart';
import 'package:star_lock/talk/starChart/proto/rbcu.pb.dart';
import 'package:star_lock/talk/starChart/proto/rbcu.pbserver.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/proto/talk_expect.pbserver.dart';
import 'package:star_lock/talk/starChart/status/star_chart_talk_status.dart';
import 'package:star_lock/tools/baseGetXController.dart';
2024-11-28 14:57:49 +08:00
import 'package:star_lock/tools/deviceInfo_utils.dart';
import 'package:star_lock/tools/storage.dart';
import 'package:uuid/uuid.dart';
class StartChartManage {
// 私有构造函数防止外部直接new对象
StartChartManage._internal();
// 单例对象
static final StartChartManage _instance = StartChartManage._internal();
final TalkeRequestOverTimeTimerManager talkeRequestOverTimeTimerManager =
TalkeRequestOverTimeTimerManager();
final TalkePingOverTimeTimerManager talkePingOverTimeTimerManager =
TalkePingOverTimeTimerManager();
final TalkDataOverTimeTimerManager talkDataOverTimeTimerManager =
TalkDataOverTimeTimerManager();
2024-11-28 14:57:49 +08:00
// 工厂构造函数,返回单例对象
factory StartChartManage() {
return _instance;
}
// 产品昵称
final String _productName = F.navTitle;
RawDatagramSocket? _udpSocket;
2025-01-06 09:52:14 +08:00
final Uuid _uuid = Uuid(); // 随机UUID用于匹配接下来的打洞会话
2024-11-28 14:57:49 +08:00
late String remoteHost = ''; // 远程主机地址(服务器返回)
late int remotePort = 0; // 远程主机端口(服务器返回)
final int localPort = 62289; // 本地端口
String localPublicHost = ''; // 本地公网ip地址
2024-11-28 14:57:49 +08:00
int heartbeatIntervalTime = 1; // 心跳包间隔时间s
2024-11-28 14:57:49 +08:00
Timer? _heartBeatTimer; // 心跳包定时器
bool _heartBeatTimerRunning = false; // 心跳包定时任务发送状态
String ToPeerId = ''; // 对端ID
String FromPeerId = ''; // 我的ID
String lockPeerId = ''; // 锁peerId
2025-02-18 14:03:44 +08:00
DeviceNetworkInfo lockNetworkInfo = DeviceNetworkInfo(); // 锁网络信息
List<LockListInfoItemEntity> lockListPeerId = []; // 锁列表peerId
// echo测试peer对端
final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH';
bool isOnlineStarChartServer = false; // 星图是否上线成功
Timer? reStartOnlineStartChartServerTimer; // 重新上线定时器
Timer? talkPingTimer; // 发送通话保持消息定时器
2024-12-09 11:06:35 +08:00
Timer? talkExpectTimer; // 发送通话预期消息定时器
Timer? talkAcceptTimer; // 重发同意接听消息定时器
2025-01-06 09:52:14 +08:00
int talkDataIntervalTime = 10; // 发送通话数据的消息间隔(ms)
int _defaultIntervalTime = 1; // 默认定时发送间隔(s)
2024-12-13 14:30:33 +08:00
Timer? talkDataTimer; // 发送通话数据消息定时器
2025-01-06 09:52:14 +08:00
Timer? rbcuInfoTimer; // p2p地址交换定时器
Timer? rbcuProbeTimer; // p2p打洞包
Timer? rbcuConfirmTimer; // p2p打洞确认包
Timer? talkHangupTimer; // 添加挂断消息定时器
Timer? talkRejectTimer; // 添加拒绝接听定时器
String _rbcuSessionId = ''; // p2p SessionId
Timer? talkRequestTimer; // 对讲请求定时器
final int maxAttempts = 15; // 最大执行次数
RbcuInfo? rbcuInfo;
RbcuProbe? rbcuProbe;
RbcuConfirm? rbcuConfirm;
2024-12-23 17:13:32 +08:00
final int _maxPayloadSize = 8 * 1024; // 分包大小
2024-12-09 11:06:35 +08:00
// 默认通话的期望数据格式
2025-04-18 10:34:42 +08:00
TalkExpectReq _defaultTalkExpect = TalkConstant.ImageExpect;
2024-12-09 11:06:35 +08:00
String relayPeerId = ''; // 中继peerId
2024-12-09 11:37:27 +08:00
// 获取 StartChartTalkStatus 的唯一实例
StartChartTalkStatus talkStatus = StartChartTalkStatus.instance;
// 星图服务初始化
Future<void> init() async {
2025-03-28 10:43:31 +08:00
if (F.isXHJ) {
return;
}
// 判断是否登录账户
final loginData = await Storage.getLoginData();
if ((isOnlineStarChartServer && _udpSocket != null) || loginData == null) {
// 如果已经上线就不进行初始化
return;
}
// 节点注册
await _clientRegister(loginData);
// 中继查询
await _relayQuery();
// 初始化udp服务
await _onlineRelayService();
// 上报
await reportInformation();
}
2024-11-28 14:57:49 +08:00
/// 客户端注册
Future<void> _clientRegister(LoginData? loginData) async {
if (loginData?.starchart?.starchartId != null) {
_log(text: '获取到星图注册节点信息:${loginData?.starchart}');
FromPeerId = loginData?.starchart?.starchartId ?? '';
} else {
_log(text: '开始注册客户端');
final StarChartRegisterNodeEntity requestStarChartRegisterNode =
await _requestStarChartRegisterNode();
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
FromPeerId = requestStarChartRegisterNode.peer!.id ?? '';
bindUserStarchart(requestStarChartRegisterNode);
}
}
//绑定星图配置
Future<void> bindUserStarchart(
StarChartRegisterNodeEntity requestStarChartRegisterNode) async {
try {
final LoginEntity entity = await ApiRepository.to.bindUserStarchart(
starchartId: requestStarChartRegisterNode.peer?.id ?? '',
starchartPeerPublicKey:
requestStarChartRegisterNode.peer?.publicKey ?? '',
starchartPeerPrivateKey:
requestStarChartRegisterNode.peer?.privateKey ?? '',
);
requestStarChartRegisterNode.peer?.id =
entity.data?.starchart?.starchartId;
if (entity.errorCode!.codeIsSuccessful) {
AppLog.log('绑定成功');
} else {
AppLog.log('绑定失败');
}
} catch (e) {
AppLog.log('Error bindUserStarchart: $e');
}
2024-11-28 14:57:49 +08:00
}
// 中继查询
Future<void> _relayQuery() async {
2024-11-28 14:57:49 +08:00
final RelayInfoEntity relayInfoEntity =
await StartChartApi.to.relayQueryInfo();
_saveRelayInfoEntityToStorage(relayInfoEntity);
if (relayInfoEntity.client_addr != null) {
localPublicHost = relayInfoEntity.client_addr!;
}
2024-12-10 16:31:56 +08:00
if (relayInfoEntity.relay_list != null &&
relayInfoEntity.relay_list!.length > 0) {
for (int i = 0; i <= relayInfoEntity.relay_list!.length; i++) {
final data = relayInfoEntity.relay_list?[i];
if (data?.peerID != FromPeerId) {
final parseUdpUrl = await _parseUdpUrl(data?.listenAddr ?? '');
remoteHost = parseUdpUrl['host'] ?? '';
remotePort = parseUdpUrl['port'] ?? '';
relayPeerId = data?.peerID ?? '';
ToPeerId = relayPeerId;
_log(text: '中继信息----》${relayInfoEntity}');
break;
}
}
2024-12-10 17:27:37 +08:00
} else {
2024-12-10 16:31:56 +08:00
_log(text: '未查询到中继信息----》');
2024-11-28 14:57:49 +08:00
}
}
void closeUdpSocket() {
if (_udpSocket != null) {
_udpSocket?.close();
2025-01-23 14:30:31 +08:00
_udpSocket = null;
2024-11-28 14:57:49 +08:00
}
}
// 初始化udp
Future<void> _onlineRelayService() async {
2024-11-28 14:57:49 +08:00
var addressIListenFrom = InternetAddress.anyIPv4;
RawDatagramSocket.bind(addressIListenFrom, localPort)
.then((RawDatagramSocket socket) {
_udpSocket = socket;
/// 广播功能
_udpSocket!.broadcastEnabled = true;
/// 设置数据接收回调
_onReceiveData(_udpSocket!, Get.context!);
// //ToDo: 增加对讲调试、正式可删除
// // 每秒重置数据速率
// Timer.periodic(Duration(seconds: 1), (Timer t) {
// UdpTalkDataHandler().resetDataRates();
// // 更新调试信息
// Provider.of<DebugInfoModel>(Get.context!, listen: false)
// .updateDebugInfo(
// UdpTalkDataHandler().getLastRecvDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastRecvPacketCount(),
// UdpTalkDataHandler().getLastSendDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastSendPacketCount(),
// );
// });
2024-11-28 14:57:49 +08:00
}).catchError((error) {
_log(text: 'Failed to bind UDP socket: $error');
});
}
// 上报信息至发现服务
Future<void> reportInformation() async {
_log(text: '上报信息至发现服务');
// 构建参数
ReportInformationData data = await _makeReportInformationData();
final response = await StartChartApi.to.reportInformation(
2024-11-28 14:57:49 +08:00
reportInformationData: data,
);
if (response.statusCode == 200) {
2024-12-09 11:37:27 +08:00
talkStatus.setInitializationCompleted();
// 发送心跳消息
_sendHeartbeatMessage();
// 发送送上线消息
await reStartOnlineStartChartServer();
}
2024-11-28 14:57:49 +08:00
}
2025-01-06 09:52:14 +08:00
// 发送RbcuInfo 地址交换消息
void _sendRbcuInfoMessage(
{required String ToPeerId, bool isResp = false}) async {
2025-01-06 09:52:14 +08:00
final uuid = _uuid.v1();
final int timestamp = DateTime.now().millisecondsSinceEpoch;
final Int64 int64Timestamp = Int64(timestamp); // 使用构造函数
// 获取本机所有ip地址和中继返回的外网地址
final List<ListenAddrData> listenAddrDataList =
await _makeListenAddrDataList();
listenAddrDataList.insert(
0, // 插入到头部
ListenAddrData(
type: ListenAddrTypeConstant.local,
address: localPublicHost + ':' + localPort.toString(),
),
);
final address = listenAddrDataList
.where((element) =>
element.type == ListenAddrTypeConstant.local) // 过滤出本地地址
.map((e) => e.address) // 转换为 List<String?>
.where((addr) => addr != null) // 过滤掉 null 值
.map(
(addr) => addr!.replaceAll(IpConstant.udpUrl, ''),
) // 去除 "udp://" 前缀
.cast<
String>(); // 转换为 Iterable<String>// 将 Iterable<String?> 转换为 Iterable<String>
_rbcuSessionId = uuid;
2025-01-06 09:52:14 +08:00
final RbcuInfo rbcuInfo = RbcuInfo(
sessionId: uuid,
name: uuid,
address: address,
time: int64Timestamp,
isResp: isResp,
2025-01-06 09:52:14 +08:00
);
2025-01-06 09:52:14 +08:00
final message = MessageCommand.genericRbcuInfoMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
rbcuInfo: rbcuInfo,
);
_sendMessage(message: message);
}
// 发送RbcuProbe
void _sendRbcuProbeMessage() async {
// 随机字符串数据
String generateRandomString(int length) {
const chars =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
final random = Random();
return String.fromCharCodes(
List.generate(
length, (index) => chars.codeUnitAt(random.nextInt(chars.length))),
);
}
if (rbcuInfo != null &&
rbcuInfo!.address != null &&
rbcuInfo!.address.length > 0) {
rbcuInfo!.address.forEach((element) {
// 拆分 element 字符串
final parts = element.split(':');
final host = parts[0]; // IP 地址
final port = int.tryParse(parts[1]) ?? 0; // 端口号,如果解析失败则默认为 0
final RbcuProbe rbcuProbe = RbcuProbe(
sessionId: _rbcuSessionId,
data: generateRandomString(100),
targetAddress: element);
final rbcuProBeBuffer = rbcuProbe.writeToBuffer();
_sendNatMessage(message: rbcuProBeBuffer, host: host, port: port);
});
}
}
2025-04-18 10:33:51 +08:00
// 发送打洞确认包
void _sendRbcuConfirmMessage() async {
RbcuConfirm(
sessionId: _rbcuSessionId,
);
}
2025-01-06 09:52:14 +08:00
// 启动定时任务
void startSendingRbcuInfoMessages({required String ToPeerId}) {
2025-01-06 09:52:14 +08:00
// 每隔 1 秒执行一次 _sendRbcuInfoMessage
rbcuInfoTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// 发送RbcuInfo 地址交换消息
_log(text: '发送RbcuInfo 地址交换消息');
_sendRbcuInfoMessage(ToPeerId: ToPeerId);
2025-01-06 09:52:14 +08:00
});
}
// 发送打洞包
void startSendingRbcuProbeTMessages() {
// 每隔 1 秒执行一次 _sendRbcuInfoMessage
rbcuProbeTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// 发送RbcuProbe
_sendRbcuProbeMessage();
});
}
// 发送打洞确认包
void startSendingRbcuConfirmTMessages() {
rbcuConfirmTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// 发送RbcuProbe
_sendRbcuConfirmMessage();
});
}
2025-01-06 09:52:14 +08:00
// 停止定时任务
void stopSendingRbcuInfoMessages() {
rbcuInfoTimer?.cancel(); // 取消定时器
rbcuInfoTimer = null;
}
// 停止定时任务
void stopSendingRbcuProBeMessages() {
rbcuProbeTimer?.cancel(); // 取消定时器
rbcuProbeTimer = null;
}
// 停止定时任务
void stopSendingRbcuConfirmMessages() {
rbcuConfirmTimer?.cancel(); // 取消定时器
rbcuConfirmTimer = null;
}
// 回复RbcuInfo
void replyRbcuInfoMessage({required String ToPeerId}) {
_sendRbcuInfoMessage(ToPeerId: ToPeerId, isResp: true);
}
2024-11-28 14:57:49 +08:00
// 发送上线消息
Future<void> _sendOnlineMessage() async {
if (isOnlineStarChartServer) {
_log(text: '星图已上线,请勿重复发送上线消息');
return;
}
2024-11-28 14:57:49 +08:00
// 组装上线消息
final message = MessageCommand.goOnlineRelay(
2024-11-28 14:57:49 +08:00
FromPeerId: FromPeerId,
ToPeerId: ToPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
2024-11-28 14:57:49 +08:00
);
await _sendMessage(message: message);
2024-11-28 14:57:49 +08:00
}
/// 启动持续发送对讲请求
void startCallRequestMessageTimer({required String ToPeerId}) async {
// 如果已经处于等待接听状态就不发送
if (talkStatus.status != TalkStatus.proactivelyCallWaitingAnswer) {
// 如果是h264则跳转至webview
2025-02-21 15:55:35 +08:00
if (_defaultTalkExpect.videoType.contains(VideoTypeE.H264)) {
Get.toNamed(
Routers.h264WebView,
);
} else {
Get.toNamed(
Routers.starChartTalkView,
);
}
}
// 启动定时器持续发送对讲请求
talkRequestTimer ??= Timer.periodic(
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) async {
await sendCallRequestMessage(ToPeerId: ToPeerId);
},
);
talkStatus.setProactivelyCallWaitingAnswer();
// 启动对讲请求应答超时判断
talkeRequestOverTimeTimerManager.start();
}
/// 停止持续发送对讲请求
void stopCallRequestMessageTimer() async {
talkRequestTimer?.cancel();
talkRequestTimer = null;
}
// 发送对讲请求消息
Future<void> sendCallRequestMessage({required String ToPeerId}) async {
// 组装上线消息
final message = MessageCommand.talkRequestMessage(
FromPeerId: FromPeerId,
ToPeerId: ToPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
}
2024-11-28 14:57:49 +08:00
2024-12-13 14:30:33 +08:00
// 发送对讲数据
// 现在的场景只有给锁板发送音频数据
2024-12-13 14:30:33 +08:00
Future<void> sendTalkDataMessage({required TalkData talkData}) async {
2025-01-14 17:57:33 +08:00
String toPeerId = ToPeerId;
2024-12-30 11:53:42 +08:00
final List<int> payload = talkData.content;
// 计算需要分多少个包发送
final int totalPackets = (payload.length / _maxPayloadSize).ceil();
// 循环遍历
for (int i = 0; i < totalPackets; i++) {
int start = i * _maxPayloadSize;
int end = (i + 1) * _maxPayloadSize;
if (end > payload.length) {
end = payload.length;
}
// 取到分包数据
2025-01-15 15:43:53 +08:00
List<int> packetTalkData = payload.sublist(start, end);
2024-12-30 11:53:42 +08:00
// 分包数据不递增messageID
final messageId =
MessageCommand.getNextMessageId(toPeerId, increment: false);
2024-12-30 11:53:42 +08:00
// 组装分包数据
final message = MessageCommand.talkDataMessage(
ToPeerId: toPeerId,
2024-12-30 11:53:42 +08:00
FromPeerId: FromPeerId,
2025-01-15 15:43:53 +08:00
talkData: TalkData(
contentType: talkData.contentType,
content: packetTalkData,
durationMs: talkData.durationMs,
),
2024-12-30 11:53:42 +08:00
SpTotal: totalPackets,
SpIndex: i + 1,
MessageId: messageId,
);
// 发送消息
await _sendMessage(message: message);
}
// 分包发送完了递增一下id
MessageCommand.getNextMessageId(toPeerId);
2024-12-13 14:30:33 +08:00
}
2024-11-28 14:57:49 +08:00
// 发送心跳包消息
void _sendHeartbeatMessage() async {
2024-11-28 14:57:49 +08:00
if (_heartBeatTimerRunning) {
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
return;
}
_heartBeatTimer ??= Timer.periodic(
Duration(
seconds: heartbeatIntervalTime,
2024-11-28 14:57:49 +08:00
),
(Timer timer) async {
final List<int> message = MessageCommand.heartbeatMessage(
FromPeerId: FromPeerId,
ToPeerId: relayPeerId,
MessageId:
MessageCommand.getNextMessageId(relayPeerId, increment: true),
);
await _sendMessage(message: message);
2024-11-28 14:57:49 +08:00
},
);
_heartBeatTimerRunning = true;
}
// 发送回声测试消息
void sendEchoMessage(
{required List<int> payload, required String toPeerId}) async {
// 计算需要分多少个包发送
final int totalPackets = (payload.length / _maxPayloadSize).ceil();
// 循环遍历
for (int i = 0; i < totalPackets; i++) {
int start = i * _maxPayloadSize;
int end = (i + 1) * _maxPayloadSize;
if (end > payload.length) {
end = payload.length;
}
// 取到分包数据
List<int> packet = payload.sublist(start, end);
2024-12-24 15:38:36 +08:00
// 分包数据不递增messageID
2024-12-23 09:56:12 +08:00
final messageId =
MessageCommand.getNextMessageId(toPeerId, increment: false);
// 组装分包数据
final message = MessageCommand.echoMessage(
ToPeerId: toPeerId,
FromPeerId: FromPeerId,
payload: packet,
SpTotal: totalPackets,
SpIndex: i + 1,
MessageId: messageId,
);
// 发送消息
await _sendMessage(message: message);
}
// 分包发送完了递增一下id
MessageCommand.getNextMessageId(toPeerId);
}
// 发送网关初始化消息
void sendGatewayResetMessage(
{required String ToPeerId, required int gatewayId}) async {
final message = MessageCommand.gatewayResetMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
gatewayId: gatewayId,
time: DateTime.now().millisecondsSinceEpoch ~/ 1000,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
}
// 发送同意接听消息
void sendTalkAcceptMessage() async {
final message = MessageCommand.talkAcceptMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
stopTalkExpectMessageTimer();
}
void _sendTalkRejectMessage() {
final message = MessageCommand.talkRejectMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
_sendMessage(message: message);
}
// 发送拒绝接听消息
void startTalkRejectMessageTimer() async {
2025-04-16 14:28:18 +08:00
try {
2025-04-18 10:33:51 +08:00
int count = 0;
final int maxCount = 10; // 最大执行次数为10秒
2025-04-16 14:28:18 +08:00
talkRejectTimer ??= Timer.periodic(
Duration(seconds: _defaultIntervalTime),
(Timer timer) async {
_sendTalkRejectMessage();
2025-04-18 10:33:51 +08:00
count++;
if (count >= maxCount) {
timer.cancel();
talkRejectTimer = null;
}
2025-04-16 14:28:18 +08:00
},
);
} catch (e) {
AppLog.log("startTalkRejectMessageTimer e:${e}");
} finally {
// 设置状态为拒绝
StartChartTalkStatus.instance.setRejected();
// 停止播放铃声
AudioPlayerManager().stopRingtone();
// 停止发送通话保持消息、通话预期数据请求
stopTalkExpectMessageTimer();
stopTalkPingMessageTimer();
stopCallRequestMessageTimer();
stopSendingRbcuInfoMessages();
stopSendingRbcuProBeMessages();
// 取消定时器
talkePingOverTimeTimerManager.cancel();
talkDataOverTimeTimerManager.cancel();
}
}
// 发送期望接受消息
void sendTalkExpectMessage({required TalkExpectReq talkExpect}) async {
final message = MessageCommand.talkExpectMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
2024-12-09 11:06:35 +08:00
talkExpect: talkExpect,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
2024-12-30 11:53:42 +08:00
// _log(text: '发送预期数据:${talkExpect}');
}
// 回复成功消息
void sendGenericRespSuccessMessage({
required String ToPeerId,
required String FromPeerId,
required int PayloadType,
required int messageId,
}) async {
if (messageId == null) {
messageId = MessageCommand.getNextMessageId(ToPeerId, increment: false);
}
final message = MessageCommand.genericRespSuccessMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
PayloadType: PayloadType,
MessageId: messageId,
);
await _sendMessage(message: message);
}
// 回复失败消息
2025-04-03 10:36:30 +08:00
void sendGenericRespErrorMessage({
required String ToPeerId,
required String FromPeerId,
required int PayloadType,
required int messageId,
}) async {
if (messageId == null) {
messageId = MessageCommand.getNextMessageId(ToPeerId, increment: false);
}
final message = MessageCommand.genericRespErrorMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
PayloadType: PayloadType,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: false),
);
await _sendMessage(message: message);
}
// 发送通话保持消息
Future<void> sendTalkPingMessage(
{required String ToPeerId, required String FromPeerId}) async {
final message = MessageCommand.talkPingMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
2024-12-30 17:26:30 +08:00
// _log(text: '发送通话保持');
}
void _sendTalkHangupMessage() async {
2024-12-10 17:27:37 +08:00
final message = MessageCommand.talkHangupMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
2024-12-10 17:27:37 +08:00
);
await _sendMessage(message: message);
}
// 发送通话中挂断消息
// 启动定时发送挂断消息
void startTalkHangupMessageTimer() {
talkHangupTimer ??= Timer.periodic(
Duration(seconds: _defaultIntervalTime),
(Timer timer) async {
_sendTalkHangupMessage();
},
);
// 设置状态为通话中挂断
StartChartTalkStatus.instance.setHangingUpDuring();
// 停止播放铃声
AudioPlayerManager().stopRingtone();
// 停止发送通话保持消息、通话预期数据请求
stopTalkExpectMessageTimer();
stopTalkPingMessageTimer();
stopCallRequestMessageTimer();
stopSendingRbcuInfoMessages();
stopSendingRbcuProBeMessages();
// 取消定时器
talkePingOverTimeTimerManager.cancel();
talkDataOverTimeTimerManager.cancel();
2024-12-10 17:27:37 +08:00
}
// 停止发送挂断消息
void stopTalkHangupMessageTimer() {
talkHangupTimer?.cancel();
talkHangupTimer = null;
}
// 停止发送挂断消息
void stopTalkRejectMessageTimer() {
talkRejectTimer?.cancel();
talkRejectTimer = null;
}
// 重新上线
Future<void> reStartOnlineStartChartServer() async {
if (isOnlineStarChartServer) {
_log(text: '星图已上线,请勿重复发送上线消息');
return;
}
reStartOnlineStartChartServerTimer ??= Timer.periodic(
Duration(
2025-01-06 09:52:14 +08:00
seconds: _defaultIntervalTime,
),
(Timer timer) async {
// 重新发送上线消息
await _sendOnlineMessage();
},
);
}
// 重新发送心跳
void reStartHeartBeat() {
stopHeartbeat();
_sendHeartbeatMessage();
}
2024-11-28 14:57:49 +08:00
// 停止定时发送心跳包
void stopHeartbeat() {
_heartBeatTimer?.cancel();
_heartBeatTimer = null; // 清除定时器引用
_heartBeatTimerRunning = false;
}
// 停止重新上线
void stopReStartOnlineStartChartServer() {
reStartOnlineStartChartServerTimer?.cancel();
reStartOnlineStartChartServerTimer = null; // 清除定时器引用
2024-11-28 14:57:49 +08:00
}
// 发送消息
Future<void> _sendMessage({required List<int> message}) async {
var result = await _udpSocket?.send(
message, InternetAddress(remoteHost), remotePort);
if (result != message.length) {
throw StartChartMessageException(
'❌Udp send data error----> $result ${message.length}');
// _udpSocket = null;
}
//ToDo: 增加对讲调试、正式可删除
// UdpTalkDataHandler().updateSendDataRate(message.length);
//
// // 更新调试信息
// Provider.of<DebugInfoModel>(Get.context!, listen: false).updateDebugInfo(
// UdpTalkDataHandler().getLastRecvDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastRecvPacketCount(),
// UdpTalkDataHandler().getLastSendDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastSendPacketCount(),
// );
2024-11-28 14:57:49 +08:00
}
// 发送消息
Future<void> _sendNatMessage(
{required List<int> message,
required String host,
required int port}) async {
_log(text: '发送nat消息');
var result = await _udpSocket?.send(message, InternetAddress(host), port);
if (result != message.length) {
throw StartChartMessageException(
'❌Udp send data error----> $result ${message.length}');
// _udpSocket = null;
}
}
2024-11-28 14:57:49 +08:00
// 请求注册节点
Future<StarChartRegisterNodeEntity> _requestStarChartRegisterNode() async {
// 获取设备信息
final Map<String, String> deviceInfo = await _getDeviceInfo();
// 发送注册节点请求
final StarChartRegisterNodeEntity response =
await StartChartApi.to.starChartRegisterNode(
product: _productName,
model: '${deviceInfo['brand']}_${deviceInfo['model']}',
name: '${deviceInfo['id']}',
unique: deviceInfo['id'] ?? Uuid().v1(),
);
return response;
}
// 保存星图注册节点信息至缓存
Future<void> _saveStarChartRegisterNodeToStorage(
StarChartRegisterNodeEntity starChartRegisterNodeEntity) async {
if (starChartRegisterNodeEntity != null) {
await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity);
final LoginData? loginData = await Storage.getLoginData();
loginData?.updateStarchart(starChartRegisterNodeEntity);
Storage.saveLoginData(loginData);
2024-11-28 14:57:49 +08:00
}
}
// 保存星图中继服务器信息至缓存
Future<void> _saveRelayInfoEntityToStorage(
RelayInfoEntity relayInfoEntity) async {
if (relayInfoEntity != null) {
await Storage.saveRelayInfo(relayInfoEntity);
}
}
// 构造上报信息数据参数
Future<ReportInformationData> _makeReportInformationData() async {
// 从缓存中获取中继信息
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
2024-11-28 14:57:49 +08:00
// 获取公钥
final String publicKey = await getPublicKey();
2024-11-28 14:57:49 +08:00
// 获取私钥
final String privateKey = await getPrivateKey();
2024-11-28 14:57:49 +08:00
// 生成签名
2025-01-06 09:52:14 +08:00
final sign = await DoSign().generateSign(
currentTimestamp: relayInfoEntity!.time ?? 0,
privateKeyHex: privateKey,
2024-11-28 14:57:49 +08:00
);
// 获取本机所有ip地址和中继返回的外网地址
final List<ListenAddrData> listenAddrDataList =
await _makeListenAddrDataList();
//
2024-11-28 14:57:49 +08:00
final RelayServiceData relayServiceData = RelayServiceData(
name: relayInfoEntity?.relay_list?[0].name ?? '',
listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '',
peers_max: relayInfoEntity?.relay_list?[0].peerMax ?? 0,
peers_current: relayInfoEntity?.relay_list?[0].peerCurrent ?? 0,
);
ReportInformationData data = ReportInformationData(
id: FromPeerId,
public_key: publicKey,
listen_addr: listenAddrDataList,
relay_service: relayServiceData,
time: relayInfoEntity.time ?? 0,
2024-11-28 14:57:49 +08:00
sign: sign,
);
return data;
}
// 解析对端数据
Future<void> analyzeInformationOtherEnd() async {
await StartChartApi.to.analyzeInformationOtherEnd(peerId: ToPeerId);
}
2024-11-28 14:57:49 +08:00
// 获取本机所有ip地址和中继返回的外网地址
Future<List<ListenAddrData>> _makeListenAddrDataList() async {
final List<ListenAddrData> listenAddrDataList = [];
final List<String> localIp = await _getAllIpAddresses();
// 从缓存中获取中继信息取出返回的客户端ip地址
2024-11-28 14:57:49 +08:00
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
if (relayInfoEntity != null && relayInfoEntity.client_addr != null) {
listenAddrDataList.add(
ListenAddrData(
type: ListenAddrTypeConstant.relay,
address: relayInfoEntity.relay_list!.last!.listenAddr!,
2024-11-28 14:57:49 +08:00
),
);
}
localIp.forEach((element) {
listenAddrDataList.add(
ListenAddrData(
type: ListenAddrTypeConstant.local,
address: IpConstant.udpUrl + element + ':' + localPort.toString(),
),
);
});
return listenAddrDataList ?? [];
}
/// 获取本机所有 IP 地址
2024-11-28 14:57:49 +08:00
Future<List<String>> _getAllIpAddresses() async {
final List<String> ipAddresses = [];
try {
final List<NetworkInterface> interfaces = await NetworkInterface.list(
includeLoopback: true,
type: InternetAddressType.any,
);
for (final interface in interfaces) {
for (final address in interface.addresses) {
// 获取原始 IP 地址
String ipAddress = address.address;
// 移除 IPv6 地址中的接口标识符(如果有)
if (ipAddress.contains('%')) {
ipAddress = ipAddress.split('%').first;
}
// 确保 IP 地址不为空且不在排除列表中
if (ipAddress.isNotEmpty &&
!IpConstant.reportExcludeIp.contains(ipAddress)) {
ipAddresses.add(ipAddress);
2024-11-28 14:57:49 +08:00
}
}
}
} catch (e) {
_log(text: '❌--->获取本机IP时出现错误: $e');
}
return ipAddresses; // 注意:这里不需要 `?? []`,因为 `ipAddresses` 已经初始化为一个空列表。
2024-11-28 14:57:49 +08:00
}
void _log({required String text}) {
AppLog.log('$_productName=====${text}');
}
/// 获取设备信息
Future<Map<String, String>> _getDeviceInfo() async {
final Map<String, String> deviceInfo =
await DeviceInfoUtils.getDeviceInfo();
return deviceInfo;
}
/// 解析 UDP URL 并提取 IP 地址和端口号
/// 解析 UDP URL 并提取 IP 地址和端口号,同时处理域名解析
Future<Map<String, dynamic>> _parseUdpUrl(String url) async {
2025-02-12 11:00:57 +08:00
final regex = RegExp(r'udp://([a-zA-Z0-9.-]+):(\d+)').firstMatch(url);
2024-11-28 14:57:49 +08:00
if (regex != null) {
2025-02-12 11:00:57 +08:00
final host = regex.group(1);
2024-11-28 14:57:49 +08:00
final portStr = regex.group(2);
final port = int.tryParse(portStr ?? '');
2025-02-12 11:00:57 +08:00
if (host != null && port != null) {
try {
// 尝试进行 DNS 解析
final List<InternetAddress> addresses =
await InternetAddress.lookup(host);
if (addresses.isEmpty) {
throw FormatException('DNS resolution failed for $host');
}
// 使用解析后的第一个 IP 地址
final String resolvedIp = addresses.first.address;
return {'host': resolvedIp, 'port': port};
} catch (e) {
throw FormatException('DNS resolution error for $host: $e');
}
2024-11-28 14:57:49 +08:00
}
}
throw FormatException('无法解析 URL 格式: $url');
}
String bytesToHex(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
}
/// 获取公钥
2024-11-28 14:57:49 +08:00
Future<String> getPublicKey() async {
final loginData = await Storage.getLoginData();
return loginData?.starchart?.starchartPeerPublicKey ?? '';
2024-11-28 14:57:49 +08:00
}
/// 获取私钥
2024-11-28 14:57:49 +08:00
Future<String> getPrivateKey() async {
final loginData = await Storage.getLoginData();
return loginData?.starchart?.starchartPeerPrivateKey ?? '';
2024-11-28 14:57:49 +08:00
}
// 接收返回的数据
void _onReceiveData(RawDatagramSocket socket, BuildContext context) {
socket.listen((RawSocketEvent event) {
if (event == RawSocketEvent.read) {
Datagram? dg = socket.receive();
try {
if (dg?.data != null) {
final deserialize = ScpMessage.deserialize(dg!.data);
// //ToDo: 增加对讲调试、正式可删除
// UdpTalkDataHandler().updateRecvDataRate(dg.data.length);
// // 更新调试信息
// Provider.of<DebugInfoModel>(context, listen: false).updateDebugInfo(
// UdpTalkDataHandler().getLastRecvDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastRecvPacketCount(),
// UdpTalkDataHandler().getLastSendDataRate() ~/ 1024, // 转换为KB
// UdpTalkDataHandler().getLastSendPacketCount(),
// );
if (deserialize != null) {
// 处理返回数据
_handleUdpResultData(deserialize);
}
2024-12-10 17:27:37 +08:00
if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) {
if (deserialize.Payload != null) {
// _log(text: 'Udp收到结构体数据---》$deserialize');
}
// _log(text: 'text---》${utf8.decode(deserialize.Payload)}');
2024-12-10 17:27:37 +08:00
}
}
2024-12-23 15:21:48 +08:00
} catch (e, stackTrace) {
throw StartChartMessageException('$e\n,$stackTrace');
}
}
});
}
// 处理udp返回的数据
void _handleUdpResultData(ScpMessage scpMessage) {
final int payloadType = scpMessage.PayloadType ?? 0;
final int messageType = scpMessage.MessageType ?? 0;
try {
2025-04-18 10:33:51 +08:00
// 记录分包数据用于统计丢包率
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) {
handler.handleReq(scpMessage);
} else if (messageType == MessageTypeConstant.Resp) {
handler.handleResp(scpMessage);
} else if (messageType == MessageTypeConstant.RealTimeData) {
handler.handleRealTimeData(scpMessage);
} else {
handler.handleInvalidReq(scpMessage);
}
} catch (e, stackTrace) {
throw StartChartMessageException('❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace');
}
}
/// 通话保持定时器
void startTalkPingMessageTimer() {
talkPingTimer ??= Timer.periodic(
Duration(
2025-01-06 09:52:14 +08:00
seconds: _defaultIntervalTime,
),
(Timer timer) async {
await sendTalkPingMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
);
},
);
}
// 停止通话保持
void stopTalkPingMessageTimer() {
talkPingTimer?.cancel();
talkPingTimer = null; // 清除定时器引用
}
2024-12-09 11:06:35 +08:00
/// 通话期望数据定时器
void startTalkExpectTimer() {
talkExpectTimer ??= Timer.periodic(
Duration(
2025-01-06 09:52:14 +08:00
seconds: _defaultIntervalTime,
2024-12-09 11:06:35 +08:00
),
(Timer timer) {
// 发送期望接受消息
sendTalkExpectMessage(
talkExpect: _defaultTalkExpect,
2024-12-09 11:06:35 +08:00
);
},
);
}
/// 通话期望数据定时器
void startTalkAcceptTimer() {
talkAcceptTimer ??= Timer.periodic(
Duration(
2025-01-06 09:52:14 +08:00
seconds: _defaultIntervalTime,
),
(Timer timer) {
sendTalkAcceptMessage();
},
);
}
void stopTalkAcceptTimer() {
talkAcceptTimer?.cancel();
talkAcceptTimer = null; // 清除定时器引用
}
2024-12-13 14:30:33 +08:00
// 停止发送通话数据
void stopTalkDataTimer() {
talkDataTimer?.cancel();
talkDataTimer = null; // 清除定时器引用
}
2024-12-09 11:06:35 +08:00
// 停止发送通话期望数据
void stopTalkExpectMessageTimer() {
talkExpectTimer?.cancel();
talkExpectTimer = null; // 清除定时器引用
}
// 重新发送预期数据
void reStartTalkExpectMessageTimer() {
stopTalkExpectMessageTimer();
startTalkExpectTimer();
}
2024-12-09 11:06:35 +08:00
/// 修改预期接收到的数据
void changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
{required TalkExpectReq talkExpect}) {
_defaultTalkExpect = talkExpect;
reStartTalkExpectMessageTimer();
2024-12-09 11:06:35 +08:00
}
2024-12-09 12:05:34 +08:00
void reSetDefaultTalkExpect() {
2025-04-18 10:34:42 +08:00
_defaultTalkExpect = TalkConstant.ImageExpect;
}
2025-02-21 15:55:35 +08:00
TalkExpectReq getDefaultTalkExpect() {
return _defaultTalkExpect;
}
/// 修改预期接收到的数据
void sendOnlyImageVideoTalkExpectData() {
final talkExpectReq = TalkExpectReq(
videoType: [VideoTypeE.IMAGE],
audioType: [],
);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: talkExpectReq);
}
/// 修改预期接收到的数据
void sendImageVideoAndG711AudioTalkExpectData() {
2025-04-18 10:34:42 +08:00
final talkExpectReq = TalkConstant.ImageExpect;
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
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,
}) async {
// 组装上线消息
final message = MessageCommand.bleMessage(
FromPeerId: FromPeerId,
2025-01-10 17:47:51 +08:00
ToPeerId: lockPeerId,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
bluetoothDeviceName: bluetoothDeviceName,
bleStructData: bleStructData,
);
await _sendMessage(message: message);
}
2024-12-09 12:05:34 +08:00
/// 销毁资源
void destruction() async {
isOnlineStarChartServer = false;
2025-01-23 14:30:31 +08:00
// 停止发送心跳消息
stopHeartbeat();
2025-01-23 14:30:31 +08:00
// 取消发送期望数据
2024-12-09 12:05:34 +08:00
stopTalkExpectMessageTimer();
// 取消发送拒绝定时器
stopTalkRejectMessageTimer();
// 取消发送挂断定时器
stopTalkHangupMessageTimer();
2025-01-23 14:30:31 +08:00
// 取消发送通话保持消息
2024-12-09 12:05:34 +08:00
stopTalkPingMessageTimer();
2025-01-23 14:30:31 +08:00
// 取消发送上线消息
2024-12-09 12:05:34 +08:00
stopReStartOnlineStartChartServer();
2025-01-23 14:30:31 +08:00
// 取消发送通话数据
2024-12-13 14:30:33 +08:00
stopTalkDataTimer();
2025-01-23 14:30:31 +08:00
// 取消p2p打洞
2025-01-06 09:52:14 +08:00
stopSendingRbcuInfoMessages();
stopSendingRbcuProBeMessages();
stopSendingRbcuConfirmMessages();
2025-01-23 14:30:31 +08:00
// 重置数据
2024-12-30 11:53:42 +08:00
_resetData();
// 删除中继缓存信息
await Storage.removerRelayInfo();
// 删除注册节点信息
await Storage.removerStarChartRegisterNodeInfo();
// 关闭udp服务
closeUdpSocket();
2025-04-18 10:33:51 +08:00
PacketLossStatistics().reset();
2024-12-09 12:05:34 +08:00
}
2024-12-30 11:53:42 +08:00
2025-01-06 09:52:14 +08:00
/// 重置数据
2024-12-30 11:53:42 +08:00
void _resetData() {
reSetDefaultTalkExpect();
isOnlineStarChartServer = false;
talkStatus.setUninitialized();
2024-12-30 11:53:42 +08:00
}
2024-11-28 14:57:49 +08:00
}