鸿蒙App手机控制TV遥控完整指南:方向键/确认键映射实现
【摘要】 引言在智能家居和物联网快速发展的时代,跨设备控制已成为提升用户体验的关键技术。其中,手机作为最常用的移动终端,控制电视等大屏设备具有天然的优势:便携性、触摸屏交互直观、网络连通性强。鸿蒙操作系统凭借其分布式软总线技术和设备虚拟化能力,为实现无缝的跨设备控制提供了强大的技术支撑。传统遥控器受限于物理按键数量和布局,难以满足复杂交互的需求。而智能手机拥有更大的屏幕、丰富的传感器和多样化的输入方式...
引言
技术背景
鸿蒙分布式技术架构
1. 分布式软总线(Distributed Soft Bus)
-
自动发现:设备间无需手动配对,可自动发现并建立连接 -
异构网络融合:支持WiFi、蓝牙、NFC等多种网络协议的统一管理 -
高带宽低时延:优化的数据传输机制,确保控制指令的实时性 -
安全可靠:内置加密和认证机制,保障通信安全
2. 设备虚拟化(Device Virtualization)
-
输入虚拟化:将手机的触摸屏、传感器映射为遥控器的按键输入 -
输出虚拟化:将TV的显示内容投射或镜像到手机屏幕 -
能力虚拟化:将TV的音视频解码、网络通信等能力暴露给手机应用
3. 分布式数据管理
-
数据流转:支持控制指令、状态信息等数据的实时同步 -
一致性保证:确保多设备间数据状态的一致性 -
冲突解决:智能处理多设备同时操作的冲突情况
TV遥控协议与技术
1. 红外遥控(IR Remote Control)
-
协议标准:NEC、RC5、RC6等编码协议 -
载波频率:通常38kHz -
传输距离:直线传输,一般5-10米 -
优缺点:成本低、功耗小,但需对准、不能穿墙
2. 射频遥控(RF Remote Control)
-
技术标准:蓝牙、2.4GHz私有协议 -
传输特性:全向传输,可穿墙,距离可达10米以上 -
交互方式:支持双向通信,可传输状态和反馈信息 -
配对机制:通常需要首次配对建立连接
3. 网络遥控(Network Remote Control)
-
通信协议:HTTP、WebSocket、TCP/UDP自定义协议 -
控制方式:通过网络API发送控制指令 -
适用场景:智能电视、电视盒子、投屏设备 -
优势:跨网段控制、支持复杂交互、可扩展性强
鸿蒙输入子系统
1. 输入事件体系
-
InputEvent:所有输入事件的基类 -
KeyEvent:按键事件,包含按键码、动作、时间戳等信息 -
MotionEvent:触摸和传感器事件 -
FocusEvent:焦点变化事件
2. 按键码定义
// 方向键
KEYCODE_DPAD_UP = 19 // 上方向键
KEYCODE_DPAD_DOWN = 20 // 下方向键
KEYCODE_DPAD_LEFT = 21 // 左方向键
KEYCODE_DPAD_RIGHT = 22 // 右方向键
KEYCODE_DPAD_CENTER = 23 // 确认/OK键
// 功能键
KEYCODE_BACK = 4 // 返回键
KEYCODE_HOME = 3 // 主页键
KEYCODE_MENU = 82 // 菜单键
KEYCODE_VOLUME_UP = 24 // 音量+
KEYCODE_VOLUME_DOWN = 25 // 音量-
KEYCODE_POWER = 26 // 电源键
3. 输入法框架集成
-
InputMethodService:输入法服务基类 -
KeyboardView:虚拟键盘视图 -
KeyCharacterMap:按键字符映射
应用场景
1. 家庭娱乐场景
-
场景描述:用户在客厅使用手机控制电视观看视频、玩游戏 -
需求特点:需要流畅的方向导航、快速响应、支持盲操 -
技术要点:低延迟控制、防误触设计、横竖屏适配
2. 智能家居控制中心
-
场景描述:手机作为智能家居入口,统一控制电视、空调、灯光等设备 -
需求特点:多设备切换、场景模式、状态反馈 -
技术要点:设备发现、状态同步、场景联动
3. 无障碍辅助场景
-
场景描述:为行动不便的用户提供便捷的电视控制方式 -
需求特点:大按键、语音辅助、简化操作 -
技术要点:无障碍API、语音识别、手势放大
4. 商务演示场景
-
场景描述:会议室中使用手机控制大屏电视进行PPT演示 -
需求特点:精准定位、激光笔效果、多人协作 -
技术要点:高精度指针、批注功能、权限管理
5. 教育学习场景
-
场景描述:在线教育中使用手机控制教学视频播放 -
需求特点:笔记标注、章节跳转、倍速控制 -
技术要点:媒体控制、交互白板、学习记录
核心特性
-
分布式设备控制:基于鸿蒙分布式能力,实现手机对TV的无缝控制 -
多协议支持:兼容红外、蓝牙、网络等多种TV控制协议 -
实时响应:优化的通信机制,确保控制指令的低延迟传输 -
双向通信:支持状态反馈和实时信息同步 -
多输入方式:支持触摸、手势、语音、加速度计等多种交互 -
安全认证:设备认证和数据加密,保障控制安全 -
自适应UI:根据不同TV型号和使用场景自动调整界面和功能 -
离线缓存:网络中断时可缓存指令,恢复后批量同步
原理流程图与原理解释
系统架构流程图
graph TB
subgraph "手机端 (控制端)"
A[用户界面层] --> B[业务逻辑层]
B --> C[分布式通信层]
C --> D[设备管理能力]
D --> E[输入事件处理]
end
subgraph "分布式软总线"
F[设备发现服务]
G[连接管理服务]
H[数据传输通道]
end
subgraph "TV端 (被控端)"
I[TV代理服务] --> J[输入事件转发]
J --> K[TV系统接口]
K --> L[电视硬件]
M[状态管理服务] --> N[状态反馈]
N --> C
end
E -->|控制指令| H
H -->|指令转发| J
K -->|执行结果| N
N -->|状态更新| A
style A fill:#e3f2fd
style I fill:#f3e5f5
style H fill:#e8f5e8
style J fill:#fff3e0
按键映射原理图
sequenceDiagram
participant User as 用户
participant PhoneUI as 手机界面
participant InputHandler as 输入处理器
participant DistributedBus as 分布式总线
participant TVProxy as TV代理服务
participant TVSystem as TV系统
User->>PhoneUI: 触摸方向键按钮
PhoneUI->>InputHandler: 生成KeyEvent
InputHandler->>InputHandler: 按键映射转换
InputHandler->>DistributedBus: 发送控制指令
DistributedBus->>TVProxy: 转发指令
TVProxy->>TVSystem: 转换为TV按键事件
TVSystem->>TVSystem: 执行相应操作
TVSystem->>TVProxy: 返回执行状态
TVProxy->>DistributedBus: 状态反馈
DistributedBus->>PhoneUI: 更新UI状态
PhoneUI->>User: 显示操作反馈
原理解释
1. 设备发现与连接建立
-
自动发现:手机端通过分布式软总线的设备发现服务扫描同一网络下的TV设备 -
能力协商:发现设备后,双方交换支持的控制能力和协议版本 -
安全认证:通过设备证书或用户授权建立可信连接 -
会话管理:维护长连接或按需建立短连接,优化资源使用
2. 输入事件处理流程
-
触摸捕获:手机界面捕获用户的触摸事件,识别为方向键或确认键操作 -
事件转换:将触摸事件转换为标准的KeyEvent,包含按键码、动作类型、时间戳 -
协议映射:根据TV支持的协议,将KeyEvent映射为对应的红外码、蓝牙指令或网络API调用 -
指令封装:按照分布式通信协议封装控制指令,添加校验和重传机制
3. 指令传输与执行
-
可靠传输:使用TCP或自定义可靠UDP协议传输控制指令,确保不丢失 -
指令队列:TV端维护指令队列,按顺序处理避免冲突 -
硬件抽象:TV代理服务将网络指令转换为系统级的输入事件 -
反馈机制:TV执行操作后返回状态信息,手机端更新UI显示操作结果
4. 状态同步与异常处理
-
状态监控:实时监控TV的连接状态、电量、信号强度等 -
冲突解决:处理多设备同时控制同一TV的情况,支持主从控制模式 -
断线重连:网络异常时自动重连,缓存未发送的指令 -
降级处理:协议不支持时降级到基础控制功能
环境准备
开发环境配置
1. DevEco Studio配置
# 下载并安装DevEco Studio 3.1+
# 配置HarmonyOS SDK路径
export HARMONYOS_SDK_HOME=/path/to/harmonyos/sdk
export PATH=$HARMONYOS_SDK_HOME:$PATH
# 安装必要的SDK组件
# - HarmonyOS SDK Platform Tools
# - HarmonyOS Device Manager
# - Previewer
2. 项目配置(build-profile.json5)
{
"app": {
"signingConfigs": [],
"compileSdkVersion": 9,
"compatibleSdkVersion": 9,
"targetSdkVersion": 9,
"bundleName": "com.example.tvremote",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
},
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": ["default"]
}
]
}
],
"deviceTypes": [
"phone",
"tablet"
]
}
3. 模块配置(module.json5)
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone"],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
},
{
"name": "RemoteControlAbility",
"srcEntry": "./ets/remotecontrol/RemoteControlAbility.ts",
"description": "TV遥控后台能力",
"icon": "$media:icon",
"label": "遥控服务",
"type": "service",
"exported": true,
"backgroundModes": ["dataTransfer"]
}
],
"extensionAbilities": [
{
"name": "DeviceDiscoveryExtAbility",
"srcEntry": "./ets/device/DeviceDiscoveryExtAbility.ts",
"type": "driver",
"exported": false
}
],
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "$string:distributed_sync_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.USE_BLUETOOTH",
"reason": "$string:bluetooth_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.ACCESS_NETWORK_STATE",
"reason": "$string:network_state_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}
目录结构设计
TVRemoteControl/
├── entry/
│ ├── src/
│ │ └── main/
│ │ ├── ets/
│ │ │ ├── entryability/
│ │ │ │ ├── EntryAbility.ts
│ │ │ │ └── RemoteControlAbility.ts
│ │ │ ├── pages/
│ │ │ │ ├── Index.ets # 主界面
│ │ │ │ ├── DeviceList.ets # 设备列表
│ │ │ │ ├── RemotePad.ets # 遥控面板
│ │ │ │ └── Settings.ets # 设置页面
│ │ │ ├── components/
│ │ │ │ ├── DirectionPad.ets # 方向键组件
│ │ │ │ ├── FunctionKeys.ets # 功能键组件
│ │ │ │ ├── StatusBar.ets # 状态栏组件
│ │ │ │ └── DeviceCard.ets # 设备卡片组件
│ │ │ ├── model/
│ │ │ │ ├── DeviceManager.ts # 设备管理
│ │ │ │ ├── KeyMapper.ts # 按键映射
│ │ │ │ ├── ProtocolHandler.ts # 协议处理
│ │ │ │ └── NetworkManager.ts # 网络管理
│ │ │ ├── utils/
│ │ │ │ ├── Logger.ts # 日志工具
│ │ │ │ ├── Constants.ts # 常量定义
│ │ │ │ └── TypeDefs.ts # 类型定义
│ │ │ └── remotecontrol/
│ │ │ ├── TVCommand.ts # TV命令定义
│ │ │ ├── InputSimulator.ts # 输入模拟
│ │ │ └── FeedbackManager.ts # 反馈管理
│ │ ├── resources/
│ │ │ ├── base/
│ │ │ │ ├── element/
│ │ │ │ │ ├── color.json
│ │ │ │ │ ├── string.json
│ │ │ │ │ └── style.json
│ │ │ │ ├── media/
│ │ │ │ │ ├── icon.png
│ │ │ │ │ ├── dpad_center.png
│ │ │ │ │ └── dpad_up.png
│ │ │ │ └── profile/
│ │ │ │ └── main_pages.json
│ │ │ └── rawfile/
│ │ │ ├── protocols/
│ │ │ │ ├── nec_protocol.json
│ │ │ │ └── bluetooth_schema.json
│ │ │ └── configs/
│ │ │ └── tv_models.json
│ │ └── module.json5
│ └── build-profile.json5
└── build.gradle
实际详细应用代码示例实现
1. 基础类型定义和常量(utils/TypeDefs.ts)
/**
* TV遥控应用类型定义文件
* 定义整个应用中使用的数据结构和常量
*/
// 设备类型枚举
export enum DeviceType {
SMART_TV = 'smart_tv', // 智能电视
TV_BOX = 'tv_box', // 电视盒子
PROJECTOR = 'projector', // 投影仪
AIR_CONDITIONER = 'air_conditioner', // 空调
SET_TOP_BOX = 'set_top_box' // 机顶盒
}
// 连接状态枚举
export enum ConnectionStatus {
DISCONNECTED = 'disconnected', // 未连接
CONNECTING = 'connecting', // 连接中
CONNECTED = 'connected', // 已连接
DISCONNECTING = 'disconnecting', // 断开中
ERROR = 'error' // 错误状态
}
// 按键动作枚举
export enum KeyAction {
DOWN = 0, // 按下
UP = 1, // 抬起
MULTIPLE = 2 // 多次点击
}
// TV品牌枚举
export enum TVBrand {
SAMSUNG = 'samsung',
LG = 'lg',
SONY = 'sony',
HISENSE = 'hisense',
TCL = 'tcl',
XIAOMI = 'xiaomi',
HUAWEI = 'huawei',
UNKNOWN = 'unknown'
}
// 按键码定义接口
export interface KeyCode {
code: number; // 按键码值
name: string; // 按键名称
description: string; // 描述
type: 'direction' | 'confirm' | 'function' | 'number'; // 按键类型
}
// 设备信息接口
export interface DeviceInfo {
deviceId: string; // 设备唯一标识
name: string; // 设备名称
type: DeviceType; // 设备类型
brand: TVBrand; // 设备品牌
model: string; // 设备型号
ipAddress?: string; // IP地址
macAddress?: string; // MAC地址
bluetoothAddr?: string; // 蓝牙地址
capabilities: string[]; // 支持的功能列表
protocolVersion: string;// 协议版本
firmwareVersion?: string; // 固件版本
lastSeen: number; // 最后发现时间
signalStrength?: number; // 信号强度
batteryLevel?: number; // 电池电量(如果是无线设备)
}
// 连接参数接口
export interface ConnectionParams {
deviceId: string;
connectionType: 'wifi' | 'bluetooth' | 'infrared';
timeout: number;
retryCount: number;
securityToken?: string;
}
// 控制指令接口
export interface ControlCommand {
commandId: string; // 指令ID
deviceId: string; // 目标设备ID
keyCode: number; // 按键码
keyAction: KeyAction; // 按键动作
timestamp: number; // 时间戳
sequenceNumber: number; // 序列号(用于去重和排序)
checksum?: string; // 校验和
}
// 设备状态接口
export interface DeviceStatus {
deviceId: string;
status: ConnectionStatus;
powerState: boolean; // 电源状态
volume: number; // 音量
channel: number; // 频道
inputSource: string; // 输入源
brightness?: number; // 亮度
temperature?: number; // 温度(空调等)
lastUpdate: number; // 最后更新时间
}
// 按键映射配置接口
export interface KeyMapping {
phoneKey: string; // 手机按键标识
tvKeyCode: number; // TV按键码
keyName: string; // 按键名称
isDirectionKey: boolean; // 是否为方向键
isConfirmKey: boolean; // 是否为确认键
repeatable: boolean; // 是否支持长按重复
longPressDelay?: number; // 长按延迟时间
}
// 协议配置接口
export interface ProtocolConfig {
protocolName: string; // 协议名称
protocolType: 'ir' | 'bt' | 'network' | 'custom';
version: string;
supportedBrands: TVBrand[];
keyMappings: Map<number, number>; // 按键映射表
connectionParams: any; // 连接参数
}
// 事件回调函数类型定义
export type DeviceFoundCallback = (device: DeviceInfo) => void;
export type ConnectionStatusCallback = (deviceId: string, status: ConnectionStatus) => void;
export type CommandResponseCallback = (command: ControlCommand, success: boolean, error?: string) => void;
export type KeyEventHandler = (keyCode: number, keyAction: KeyAction) => void;
// 应用配置接口
export interface AppConfig {
discoveryTimeout: number; // 设备发现超时时间
connectionTimeout: number; // 连接超时时间
commandRetryCount: number; // 指令重试次数
heartbeatInterval: number; // 心跳间隔
keyRepeatDelay: number; // 按键重复延迟
keyRepeatInterval: number; // 按键重复间隔
enableLogging: boolean; // 是否启用日志
logLevel: 'debug' | 'info' | 'warn' | 'error';
themeColor: string; // 主题颜色
hapticFeedback: boolean; // 触觉反馈
}
// 错误类型定义
export enum ErrorType {
DEVICE_NOT_FOUND = 'device_not_found',
CONNECTION_FAILED = 'connection_failed',
COMMAND_TIMEOUT = 'command_timeout',
PROTOCOL_ERROR = 'protocol_error',
PERMISSION_DENIED = 'permission_denied',
NETWORK_UNAVAILABLE = 'network_unavailable',
BLUETOOTH_DISABLED = 'bluetooth_disabled'
}
export interface AppError {
type: ErrorType;
message: string;
code: number;
details?: any;
timestamp: number;
}
// 常量定义
export const CONSTANTS = {
// 默认按键映射
DEFAULT_KEY_MAPPINGS: {
'dpad_up': { tvKeyCode: 19, keyName: '上方向键', isDirectionKey: true },
'dpad_down': { tvKeyCode: 20, keyName: '下方向键', isDirectionKey: true },
'dpad_left': { tvKeyCode: 21, keyName: '左方向键', isDirectionKey: true },
'dpad_right': { tvKeyCode: 22, keyName: '右方向键', isDirectionKey: true },
'dpad_center': { tvKeyCode: 23, keyName: '确认键', isConfirmKey: true },
'back': { tvKeyCode: 4, keyName: '返回键', isFunctionKey: true },
'home': { tvKeyCode: 3, keyName: '主页键', isFunctionKey: true },
'menu': { tvKeyCode: 82, keyName: '菜单键', isFunctionKey: true },
'volume_up': { tvKeyCode: 24, keyName: '音量+', isFunctionKey: true },
'volume_down': { tvKeyCode: 25, keyName: '音量-', isFunctionKey: true }
} as Record<string, KeyMapping>,
// 默认配置
DEFAULT_CONFIG: {
discoveryTimeout: 10000,
connectionTimeout: 5000,
commandRetryCount: 3,
heartbeatInterval: 30000,
keyRepeatDelay: 500,
keyRepeatInterval: 100,
enableLogging: true,
logLevel: 'info' as const,
themeColor: '#007DFF',
hapticFeedback: true
} as AppConfig,
// 网络相关常量
NETWORK: {
MULTICAST_ADDRESS: '224.0.0.1',
MULTICAST_PORT: 8888,
BROADCAST_INTERVAL: 3000,
TCP_PORT: 8899,
WEB_SOCKET_URL: 'ws://{ip}:{port}/remote',
HTTP_API_BASE: 'http://{ip}:{port}/api/v1'
},
// 蓝牙相关常量
BLUETOOTH: {
SERVICE_UUID: '00001101-0000-1000-8000-00805F9B34FB',
CHARACTERISTIC_UUID: '00001102-0000-1000-8000-00805F9B34FB',
SCAN_PERIOD: 10000,
CONNECT_TIMEOUT: 10000
},
// 红外协议常量
IR_PROTOCOL: {
NEC_FREQUENCY: 38000,
RC5_FREQUENCY: 36000,
CARRIER_DUTY: 0.33,
LEADER_PULSE_MIN: 8000,
LEADER_PULSE_MAX: 12000
}
} as const;
2. 日志工具类(utils/Logger.ts)
/**
* 日志工具类
* 提供分级日志记录、文件输出、远程上报等功能
*/
import { LogLevel, CONSTANTS } from './TypeDefs';
export class Logger {
private static instance: Logger;
private logLevel: LogLevel;
private enableConsole: boolean;
private enableFile: boolean;
private enableRemote: boolean;
private logBuffer: string[];
private maxBufferSize: number;
private logFileName: string;
private constructor() {
// 从配置初始化日志级别
this.logLevel = CONSTANTS.DEFAULT_CONFIG.enableLogging ?
CONSTANTS.DEFAULT_CONFIG.logLevel : 'error';
this.enableConsole = true;
this.enableFile = false; // 文件日志需要额外权限
this.enableRemote = false; // 远程日志需要网络权限
this.logBuffer = [];
this.maxBufferSize = 1000;
this.logFileName = 'tv_remote.log';
// 初始化文件日志(如果支持)
this.initFileLogging();
}
/**
* 获取Logger单例
*/
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
/**
* 设置日志级别
* @param level 日志级别
*/
public setLogLevel(level: LogLevel): void {
this.logLevel = level;
this.info('Logger', `日志级别设置为: ${level}`);
}
/**
* 调试级别日志
* @param tag 标签
* @param message 消息
* @param args 附加参数
*/
public debug(tag: string, message: string, ...args: any[]): void {
if (this.shouldLog('debug')) {
this.log('DEBUG', tag, message, args);
}
}
/**
* 信息级别日志
* @param tag 标签
* @param message 消息
* @param args 附加参数
*/
public info(tag: string, message: string, ...args: any[]): void {
if (this.shouldLog('info')) {
this.log('INFO', tag, message, args);
}
}
/**
* 警告级别日志
* @param tag 标签
* @param message 消息
* @param args 附加参数
*/
public warn(tag: string, message: string, ...args: any[]): void {
if (this.shouldLog('warn')) {
this.log('WARN', tag, message, args);
}
}
/**
* 错误级别日志
* @param tag 标签
* @param message 消息
* @param args 附加参数
*/
public error(tag: string, message: string, ...args: any[]): void {
if (this.shouldLog('error')) {
this.log('ERROR', tag, message, args);
// 错误信息始终输出到控制台
if (!this.enableConsole) {
console.error(`[${tag}] ${message}`, ...args);
}
}
}
/**
* 关键级别日志(最高级别)
* @param tag 标签
* @param message 消息
* @param args 附加参数
*/
public fatal(tag: string, message: string, ...args: any[]): void {
this.log('FATAL', tag, message, args);
// 关键错误可以考虑终止应用或重启服务
console.error(`[FATAL] [${tag}] ${message}`, ...args);
}
/**
* 性能计时开始
* @param tag 标签
* @param operation 操作名称
*/
public timeStart(tag: string, operation: string): void {
if (this.shouldLog('debug')) {
const startTime = Date.now();
const timerKey = `${tag}_${operation}`;
(globalThis as any)[timerKey] = startTime;
this.debug(tag, `开始计时: ${operation}`);
}
}
/**
* 性能计时结束
* @param tag 标签
* @param operation 操作名称
*/
public timeEnd(tag: string, operation: string): void {
if (this.shouldLog('debug')) {
const timerKey = `${tag}_${operation}`;
const startTime = (globalThis as any)[timerKey];
if (startTime) {
const endTime = Date.now();
const duration = endTime - startTime;
this.debug(tag, `计时结束: ${operation}, 耗时: ${duration}ms`);
delete (globalThis as any)[timerKey];
} else {
this.warn(tag, `未找到计时起点: ${operation}`);
}
}
}
/**
* 记录用户操作
* @param userId 用户ID
* @param action 操作
* @param details 详细信息
*/
public logUserAction(userId: string, action: string, details?: any): void {
const logEntry = {
type: 'USER_ACTION',
userId,
action,
details,
timestamp: Date.now(),
sessionId: this.getSessionId()
};
this.info('Analytics', `用户操作: ${action}`, logEntry);
// 如果启用远程日志,上报到服务器
if (this.enableRemote) {
this.sendRemoteLog(logEntry);
}
}
/**
* 记录性能指标
* @param metric 指标名称
* @param value 指标值
* @param tags 标签
*/
public logMetric(metric: string, value: number, tags?: Record<string, string>): void {
const logEntry = {
type: 'METRIC',
metric,
value,
tags,
timestamp: Date.now()
};
this.debug('Performance', `性能指标: ${metric}=${value}`, logEntry);
}
/**
* 清空日志缓冲区
*/
public flushBuffer(): void {
if (this.logBuffer.length > 0) {
this.writeToFile(this.logBuffer.join('\n'));
this.logBuffer = [];
}
}
/**
* 获取日志统计信息
*/
public getStats(): Record<string, any> {
return {
logLevel: this.logLevel,
bufferSize: this.logBuffer.length,
maxBufferSize: this.maxBufferSize,
enableConsole: this.enableConsole,
enableFile: this.enableFile,
enableRemote: this.enableRemote
};
}
/**
* 判断是否应该记录指定级别的日志
*/
private shouldLog(level: LogLevel): boolean {
const levels: Record<LogLevel, number> = {
'debug': 0,
'info': 1,
'warn': 2,
'error': 3,
'fatal': 4
};
return levels[level] >= levels[this.logLevel];
}
/**
* 核心日志方法
*/
private log(level: string, tag: string, message: string, args: any[]): void {
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] [${level}] [${tag}] ${message}`;
// 格式化附加参数
let formattedArgs = '';
if (args.length > 0) {
try {
formattedArgs = ' ' + args.map(arg => {
if (typeof arg === 'object') {
try {
return JSON.stringify(arg);
} catch (e) {
return '[Unserializable Object]';
}
}
return String(arg);
}).join(', ');
} catch (error) {
formattedArgs = ' [Error formatting arguments]';
}
}
const fullMessage = logEntry + formattedArgs;
// 输出到控制台
if (this.enableConsole) {
switch (level) {
case 'DEBUG':
console.debug(fullMessage);
break;
case 'INFO':
console.info(fullMessage);
break;
case 'WARN':
console.warn(fullMessage);
break;
case 'ERROR':
case 'FATAL':
console.error(fullMessage);
break;
default:
console.log(fullMessage);
}
}
// 添加到缓冲区
this.logBuffer.push(fullMessage);
// 缓冲区满时写入文件
if (this.logBuffer.length >= this.maxBufferSize) {
this.flushBuffer();
}
// 发送到远程服务器
if (this.enableRemote && (level === 'ERROR' || level === 'FATAL')) {
this.sendRemoteLog({
level,
tag,
message,
args,
timestamp: Date.now()
});
}
}
/**
* 初始化文件日志
*/
private initFileLogging(): void {
// 检查是否在支持文件系统的环境中
try {
// 在鸿蒙应用中,需要使用@ohos.file.fs API
// 这里简化处理,实际实现需要申请文件系统权限
this.enableFile = false;
this.debug('Logger', '文件系统不可用,文件日志已禁用');
} catch (error) {
this.enableFile = false;
this.warn('Logger', '初始化文件日志失败', error);
}
}
/**
* 写入文件
*/
private writeToFile(content: string): void {
if (!this.enableFile) return;
// 实际的文件写入实现
// 需要使用鸿蒙的文件系统API
this.debug('Logger', `写入日志文件: ${content.length} 字符`);
}
/**
* 发送远程日志
*/
private sendRemoteLog(logData: any): void {
if (!this.enableRemote) return;
// 异步发送日志到远程服务器
// 使用@ohos.net.http或@ohos.net.socket
this.debug('Logger', '发送远程日志', logData);
}
/**
* 获取会话ID
*/
private getSessionId(): string {
// 生成或获取当前会话ID
if (!(globalThis as any).sessionId) {
(globalThis as any).sessionId = this.generateSessionId();
}
return (globalThis as any).sessionId;
}
/**
* 生成会话ID
*/
private generateSessionId(): string {
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
}
// 导出便捷方法
export const logger = Logger.getInstance();
// 全局便捷方法
export const LOG_DEBUG = (tag: string, message: string, ...args: any[]) =>
logger.debug(tag, message, ...args);
export const LOG_INFO = (tag: string, message: string, ...args: any[]) =>
logger.info(tag, message, ...args);
export const LOG_WARN = (tag: string, message: string, ...args: any[]) =>
logger.warn(tag, message, ...args);
export const LOG_ERROR = (tag: string, message: string, ...args: any[]) =>
logger.error(tag, message, ...args);
export const LOG_FATAL = (tag: string, message: string, ...args: any[]) =>
logger.fatal(tag, message, ...args);
3. 设备管理类(model/DeviceManager.ts)
/**
* 设备管理类
* 负责TV设备的发现、连接、状态管理等功能
*/
import deviceManager from '@ohos.distributedHardware.deviceManager';
import {
DeviceInfo,
DeviceType,
ConnectionStatus,
TVBrand,
DeviceFoundCallback,
ConnectionStatusCallback,
AppError,
ErrorType,
CONSTANTS,
logger
} from '../utils/TypeDefs';
import { NetworkManager } from './NetworkManager';
import { ProtocolHandler } from './ProtocolHandler';
export class DeviceManager {
private static instance: DeviceManager;
private deviceManager: deviceManager.DeviceManager;
private discoveredDevices: Map<string, DeviceInfo>;
private connectedDevices: Map<string, DeviceInfo>;
private connectionCallbacks: Map<string, ConnectionStatusCallback[]>;
private deviceFoundCallbacks: DeviceFoundCallback[];
private networkManager: NetworkManager;
private protocolHandler: ProtocolHandler;
private isInitialized: boolean;
private discoveryTimer: number | null;
private heartbeatTimers: Map<string, number>;
private constructor() {
this.discoveredDevices = new Map();
this.connectedDevices = new Map();
this.connectionCallbacks = new Map();
this.deviceFoundCallbacks = [];
this.networkManager = NetworkManager.getInstance();
this.protocolHandler = ProtocolHandler.getInstance();
this.isInitialized = false;
this.discoveryTimer = null;
this.heartbeatTimers = new Map();
logger.info('DeviceManager', '设备管理类实例化');
}
/**
* 获取DeviceManager单例
*/
public static getInstance(): DeviceManager {
if (!DeviceManager.instance) {
DeviceManager.instance = new DeviceManager();
}
return DeviceManager.instance;
}
/**
* 初始化设备管理器
*/
public async init(): Promise<boolean> {
try {
logger.info('DeviceManager', '开始初始化设备管理器');
// 初始化设备发现服务
this.deviceManager = deviceManager.createDeviceManager('com.example.tvremote');
// 注册设备状态变化监听
this.registerDeviceListeners();
// 初始化网络管理器
await this.networkManager.init();
// 初始化协议处理器
this.protocolHandler.init();
this.isInitialized = true;
logger.info('DeviceManager', '设备管理器初始化成功');
return true;
} catch (error) {
logger.error('DeviceManager', '设备管理器初始化失败', error);
this.handleError('INIT_FAILED', '设备管理器初始化失败', error);
return false;
}
}
/**
* 注册设备监听器
*/
private registerDeviceListeners(): void {
if (!this.deviceManager) {
logger.error('DeviceManager', '设备管理器未初始化,无法注册监听器');
return;
}
try {
// 注册设备上线监听
this.deviceManager.on('deviceOnline', (data) => {
logger.info('DeviceManager', '设备上线', data);
this.handleDeviceOnline(data);
});
// 注册设备下线监听
this.deviceManager.on('deviceOffline', (data) => {
logger.info('DeviceManager', '设备下线', data);
this.handleDeviceOffline(data);
});
// 注册分布式设备状态变化监听
this.deviceManager.on('deviceStateChange', (data) => {
logger.debug('DeviceManager', '设备状态变化', data);
this.handleDeviceStateChange(data);
});
logger.debug('DeviceManager', '设备监听器注册完成');
} catch (error) {
logger.error('DeviceManager', '注册设备监听器失败', error);
}
}
/**
* 开始设备发现
* @param timeout 发现超时时间
*/
public startDeviceDiscovery(timeout: number = CONSTANTS.DEFAULT_CONFIG.discoveryTimeout): void {
if (!this.isInitialized) {
logger.error('DeviceManager', '设备管理器未初始化,无法开始设备发现');
return;
}
logger.info('DeviceManager', `开始设备发现,超时时间: ${timeout}ms`);
// 清除之前的定时器
if (this.discoveryTimer) {
clearTimeout(this.discoveryTimer);
this.discoveryTimer = null;
}
// 清空之前发现的设备
this.discoveredDevices.clear();
try {
// 通过分布式设备管理器发现设备
this.discoverDistributedDevices();
// 通过网络扫描发现TV设备
this.discoverNetworkDevices();
// 通过蓝牙发现设备
this.discoverBluetoothDevices();
// 设置发现超时
this.discoveryTimer = setTimeout(() => {
logger.info('DeviceManager', '设备发现超时');
this.stopDeviceDiscovery();
}, timeout) as unknown as number;
} catch (error) {
logger.error('DeviceManager', '开始设备发现失败', error);
this.handleError('DISCOVERY_FAILED', '设备发现失败', error);
}
}
/**
* 停止设备发现
*/
public stopDeviceDiscovery(): void {
logger.info('DeviceManager', '停止设备发现');
if (this.discoveryTimer) {
clearTimeout(this.discoveryTimer);
this.discoveryTimer = null;
}
// 停止各类设备发现
this.stopNetworkDiscovery();
this.stopBluetoothDiscovery();
}
/**
* 发现分布式设备
*/
private discoverDistributedDevices(): void {
try {
// 获取已经配对的设备
this.deviceManager.getTrustedDeviceListSync().forEach(device => {
const deviceInfo = this.convertToDeviceInfo(device);
if (deviceInfo) {
this.addDiscoveredDevice(deviceInfo);
}
});
// 开始发现新设备
this.deviceManager.startDeviceDiscovery({
discoverTargetType: [
deviceManager.DiscoverTargetType.DEVICE_TYPE_SMART_TV,
deviceManager.DiscoverTargetType.DEVICE_TYPE_SETTOP_BOX
],
strategy: deviceManager.DiscoverStrategy.AUTO
});
logger.debug('DeviceManager', '分布式设备发现已启动');
} catch (error) {
logger.error('DeviceManager', '分布式设备发现失败', error);
}
}
/**
* 发现网络设备
*/
private discoverNetworkDevices(): void {
try {
// 使用网络管理器发现TV设备
this.networkManager.startDiscovery((deviceInfo: DeviceInfo) => {
this.addDiscoveredDevice(deviceInfo);
});
logger.debug('DeviceManager', '网络设备发现已启动');
} catch (error) {
logger.error('DeviceManager', '网络设备发现失败', error);
}
}
/**
* 发现蓝牙设备
*/
private discoverBluetoothDevices(): void {
try {
// 蓝牙设备发现实现
// 需要使用@ohos.bluetooth模块
logger.debug('DeviceManager', '蓝牙设备发现已启动');
} catch (error) {
logger.error('DeviceManager', '蓝牙设备发现失败', error);
}
}
/**
* 停止网络发现
*/
private stopNetworkDiscovery(): void {
try {
this.networkManager.stopDiscovery();
logger.debug('DeviceManager', '网络发现已停止');
} catch (error) {
logger.error('DeviceManager', '停止网络发现失败', error);
}
}
/**
* 停止蓝牙发现
*/
private stopBluetoothDiscovery(): void {
try {
// 停止蓝牙扫描
logger.debug('DeviceManager', '蓝牙发现已停止');
} catch (error) {
logger.error('DeviceManager', '停止蓝牙发现失败', error);
}
}
/**
* 添加发现的设备
*/
private addDiscoveredDevice(deviceInfo: DeviceInfo): void {
// 检查设备是否已存在
const existingDevice = this.discoveredDevices.get(deviceInfo.deviceId);
if (existingDevice) {
// 更新现有设备信息
this.updateDeviceInfo(existingDevice, deviceInfo);
} else {
// 添加新设备
this.discoveredDevices.set(deviceInfo.deviceId, deviceInfo);
logger.info('DeviceManager', `发现新设备: ${deviceInfo.name}`, deviceInfo);
// 通知设备发现回调
this.notifyDeviceFound(deviceInfo);
}
}
/**
* 更新设备信息
*/
private updateDeviceInfo(existing: DeviceInfo, updated: DeviceInfo): void {
// 保留原有的连接状态等信息
updated.status = existing.status;
updated.lastSeen = Date.now();
this.discoveredDevices.set(existing.deviceId, updated);
logger.debug('DeviceManager', `更新设备信息: ${updated.name}`);
}
/**
* 处理设备上线事件
*/
private handleDeviceOnline(data: any): void {
try {
const deviceInfo = this.convertToDeviceInfo(data);
if (deviceInfo) {
this.addDiscoveredDevice(deviceInfo);
// 如果设备之前已连接,尝试重新连接
if (this.connectedDevices.has(deviceInfo.deviceId)) {
logger.info('DeviceManager', `设备重新上线,尝试重连: ${deviceInfo.name}`);
this.reconnectDevice(deviceInfo.deviceId);
}
}
} catch (error) {
logger.error('DeviceManager', '处理设备上线事件失败', error);
}
}
/**
* 处理设备下线事件
*/
private handleDeviceOffline(data: any): void {
try {
const deviceId = data.deviceId;
logger.info('DeviceManager', `设备下线: ${deviceId}`);
// 更新设备状态
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.DISCONNECTED);
// 清理心跳定时器
this.stopHeartbeat(deviceId);
// 从已连接设备列表中移除
this.connectedDevices.delete(deviceId);
} catch (error) {
logger.error('DeviceManager', '处理设备下线事件失败', error);
}
}
/**
* 处理设备状态变化事件
*/
private handleDeviceStateChange(data: any): void {
try {
const deviceId = data.deviceId;
const state = data.state;
logger.debug('DeviceManager', `设备状态变化: ${deviceId}, 状态: ${state}`);
// 根据状态变化更新设备信息
switch (state) {
case 'online':
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.CONNECTED);
break;
case 'offline':
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.DISCONNECTED);
break;
case 'unpaired':
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.DISCONNECTED);
break;
}
} catch (error) {
logger.error('DeviceManager', '处理设备状态变化失败', error);
}
}
/**
* 转换设备信息格式
*/
private convertToDeviceInfo(device: any): DeviceInfo | null {
try {
// 根据设备类型转换为标准格式
const deviceInfo: DeviceInfo = {
deviceId: device.uuid || device.deviceId || this.generateDeviceId(),
name: device.deviceName || device.name || '未知设备',
type: this.inferDeviceType(device),
brand: this.inferBrand(device),
model: device.model || '未知型号',
ipAddress: device.ipAddress,
macAddress: device.macAddress,
bluetoothAddr: device.bluetoothAddr,
capabilities: this.extractCapabilities(device),
protocolVersion: device.protocolVersion || '1.0',
firmwareVersion: device.firmwareVersion,
lastSeen: Date.now(),
signalStrength: device.rssi || device.signalStrength
};
return deviceInfo;
} catch (error) {
logger.error('DeviceManager', '转换设备信息失败', error);
return null;
}
}
/**
* 推断设备类型
*/
private inferDeviceType(device: any): DeviceType {
const deviceType = device.deviceType || device.type || '';
const deviceName = device.deviceName || device.name || '';
if (deviceType.includes('tv') || deviceName.includes('TV') || deviceName.includes('电视')) {
return DeviceType.SMART_TV;
} else if (deviceType.includes('box') || deviceName.includes('盒子') || deviceName.includes('Box')) {
return DeviceType.TV_BOX;
} else if (deviceType.includes('projector') || deviceName.includes('投影')) {
return DeviceType.PROJECTOR;
} else if (deviceType.includes('ac') || deviceName.includes('空调')) {
return DeviceType.AIR_CONDITIONER;
} else {
return DeviceType.SMART_TV; // 默认为智能电视
}
}
/**
* 推断设备品牌
*/
private inferBrand(device: any): TVBrand {
const deviceName = device.deviceName || device.name || '';
const manufacturer = device.manufacturer || '';
const brandMap: Record<string, TVBrand> = {
'samsung': TVBrand.SAMSUNG,
'三星': TVBrand.SAMSUNG,
'lg': TVBrand.LG,
'乐金': TVBrand.LG,
'sony': TVBrand.SONY,
'索尼': TVBrand.SONY,
'hisense': TVBrand.HISENSE,
'海信': TVBrand.HISENSE,
'tcl': TVBrand.TCL,
'tcl电视': TVBrand.TCL,
'xiaomi': TVBrand.XIAOMI,
'小米': TVBrand.XIAOMI,
'huawei': TVBrand.HUAWEI,
'华为': TVBrand.HUAWEI,
'荣耀': TVBrand.HUAWEI
};
// 检查制造商
if (manufacturer.toLowerCase() in brandMap) {
return brandMap[manufacturer.toLowerCase()];
}
// 检查设备名称
const lowerName = deviceName.toLowerCase();
for (const [key, brand] of Object.entries(brandMap)) {
if (lowerName.includes(key)) {
return brand;
}
}
return TVBrand.UNKNOWN;
}
/**
* 提取设备能力
*/
private extractCapabilities(device: any): string[] {
const capabilities: string[] = [];
// 基础能力
capabilities.push('power_control', 'volume_control', 'channel_control');
// 根据设备类型添加特定能力
const deviceType = this.inferDeviceType(device);
switch (deviceType) {
case DeviceType.SMART_TV:
capabilities.push('app_launch', 'input_source', 'screen_mirror');
break;
case DeviceType.TV_BOX:
capabilities.push('app_launch', 'storage_access', 'cast_support');
break;
case DeviceType.PROJECTOR:
capabilities.push('focus_adjust', 'keystone_correction', 'lamp_status');
break;
}
// 根据设备品牌添加特定能力
const brand = this.inferBrand(device);
switch (brand) {
case TVBrand.SAMSUNG:
capabilities.push('smart_hub', 'bixby_voice');
break;
case TVBrand.LG:
capabilities.push('webos_control', 'thinq_ai');
break;
case TVBrand.SONY:
capabilities.push('android_tv', 'google_assistant');
break;
case TVBrand.XIAOMI:
capabilities.push('patchwall', 'miui_tv');
break;
}
return capabilities;
}
/**
* 生成设备ID
*/
private generateDeviceId(): string {
return 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
/**
* 连接设备
* @param deviceId 设备ID
* @param params 连接参数
*/
public async connectDevice(deviceId: string, params?: any): Promise<boolean> {
try {
logger.info('DeviceManager', `开始连接设备: ${deviceId}`);
// 检查设备是否存在
const deviceInfo = this.discoveredDevices.get(deviceId);
if (!deviceInfo) {
throw new Error(`设备未找到: ${deviceId}`);
}
// 检查是否已连接
if (this.connectedDevices.has(deviceId)) {
logger.warn('DeviceManager', `设备已连接: ${deviceId}`);
return true;
}
// 更新连接状态
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.CONNECTING);
// 根据设备类型选择合适的连接方式
const connectionSuccess = await this.establishConnection(deviceInfo, params);
if (connectionSuccess) {
// 连接成功
this.connectedDevices.set(deviceId, deviceInfo);
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.CONNECTED);
// 启动心跳检测
this.startHeartbeat(deviceId);
logger.info('DeviceManager', `设备连接成功: ${deviceInfo.name}`);
return true;
} else {
// 连接失败
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.ERROR);
throw new Error(`连接设备失败: ${deviceId}`);
}
} catch (error) {
logger.error('DeviceManager', `连接设备失败: ${deviceId}`, error);
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.ERROR);
this.handleError('CONNECTION_FAILED', `连接设备失败: ${deviceId}`, error);
return false;
}
}
/**
* 建立设备连接
*/
private async establishConnection(deviceInfo: DeviceInfo, params?: any): Promise<boolean> {
try {
logger.debug('DeviceManager', `建立连接: ${deviceInfo.name}`, deviceInfo);
// 根据设备支持的协议选择连接方式
if (deviceInfo.capabilities.includes('wifi_control') || deviceInfo.ipAddress) {
// 优先使用网络连接
return await this.networkManager.connectDevice(deviceInfo, params);
} else if (deviceInfo.capabilities.includes('bluetooth_control') || deviceInfo.bluetoothAddr) {
// 使用蓝牙连接
return await this.connectViaBluetooth(deviceInfo, params);
} else {
// 使用分布式连接
return await this.connectViaDistributed(deviceInfo, params);
}
} catch (error) {
logger.error('DeviceManager', `建立连接失败: ${deviceInfo.deviceId}`, error);
return false;
}
}
/**
* 通过网络连接设备
*/
private async connectViaNetwork(deviceInfo: DeviceInfo, params?: any): Promise<boolean> {
return await this.networkManager.connectDevice(deviceInfo, params);
}
/**
* 通过蓝牙连接设备
*/
private async connectViaBluetooth(deviceInfo: DeviceInfo, params?: any): Promise<boolean> {
try {
// 蓝牙连接实现
// 需要使用@ohos.bluetooth模块
logger.info('DeviceManager', `通过蓝牙连接设备: ${deviceInfo.name}`);
// 模拟连接过程
await this.delay(2000);
return true;
} catch (error) {
logger.error('DeviceManager', `蓝牙连接失败: ${deviceInfo.deviceId}`, error);
return false;
}
}
/**
* 通过分布式连接设备
*/
private async connectViaDistributed(deviceInfo: DeviceInfo, params?: any): Promise<boolean> {
try {
logger.info('DeviceManager', `通过分布式连接设备: ${deviceInfo.name}`);
// 配对设备(如果未配对)
const device = this.findDistributedDevice(deviceInfo.deviceId);
if (device && !device.isTrusted) {
await this.pairDevice(device);
}
// 连接设备
// 实际实现需要调用分布式能力接口
await this.delay(1500);
return true;
} catch (error) {
logger.error('DeviceManager', `分布式连接失败: ${deviceInfo.deviceId}`, error);
return false;
}
}
/**
* 查找分布式设备
*/
private findDistributedDevice(deviceId: string): any {
try {
const devices = this.deviceManager.getTrustedDeviceListSync();
return devices.find(device => device.deviceId === deviceId);
} catch (error) {
logger.error('DeviceManager', '查找分布式设备失败', error);
return null;
}
}
/**
* 配对设备
*/
private async pairDevice(device: any): Promise<void> {
return new Promise((resolve, reject) => {
try {
this.deviceManager.bindTarget(device.deviceId, (err, data) => {
if (err) {
logger.error('DeviceManager', '设备配对失败', err);
reject(err);
} else {
logger.info('DeviceManager', '设备配对成功', data);
resolve();
}
});
} catch (error) {
reject(error);
}
});
}
/**
* 断开设备连接
* @param deviceId 设备ID
*/
public async disconnectDevice(deviceId: string): Promise<boolean> {
try {
logger.info('DeviceManager', `断开设备连接: ${deviceId}`);
// 检查设备是否已连接
if (!this.connectedDevices.has(deviceId)) {
logger.warn('DeviceManager', `设备未连接: ${deviceId}`);
return true;
}
// 停止心跳
this.stopHeartbeat(deviceId);
// 断开连接
const deviceInfo = this.connectedDevices.get(deviceId)!;
await this.closeConnection(deviceInfo);
// 更新状态
this.connectedDevices.delete(deviceId);
this.updateDeviceConnectionStatus(deviceId, ConnectionStatus.DISCONNECTED);
logger.info('DeviceManager', `设备断开连接成功: ${deviceInfo.name}`);
return true;
} catch (error) {
logger.error('DeviceManager', `断开设备连接失败: ${deviceId}`, error);
this.handleError('DISCONNECT_FAILED', `断开设备连接失败: ${deviceId}`, error);
return false;
}
}
/**
* 关闭设备连接
*/
private async closeConnection(deviceInfo: DeviceInfo): Promise<void> {
try {
if (deviceInfo.ipAddress) {
await this.networkManager.disconnectDevice(deviceInfo.deviceId);
} else if (deviceInfo.bluetoothAddr) {
// 关闭蓝牙连接
await this.disconnectBluetooth(deviceInfo.deviceId);
} else {
// 关闭分布式连接
await this.unbindDistributedDevice(deviceInfo.deviceId);
}
} catch (error) {
logger.error('DeviceManager', `关闭连接失败: ${deviceInfo.deviceId}`, error);
throw error;
}
}
/**
* 断开蓝牙连接
*/
private async disconnectBluetooth(deviceId: string): Promise<void> {
// 蓝牙断开连接实现
await this.delay(500);
}
/**
* 解绑分布式设备
*/
private async unbindDistributedDevice(deviceId: string): Promise<void> {
return new Promise((resolve, reject) => {
try {
this.deviceManager.releaseTarget(deviceId, (err, data) => {
if (err) {
logger.error('DeviceManager', '解绑设备失败', err);
reject(err);
} else {
logger.info('DeviceManager', '解绑设备成功', data);
resolve();
}
});
} catch (error) {
reject(error);
}
});
}
/**
* 重新连接设备
*/
private async reconnectDevice(deviceId: string): Promise<boolean> {
logger.info('DeviceManager', `尝试重新连接设备: ${deviceId}`);
// 延迟一段时间后重连
await this.delay(1000);
return await this.connectDevice(deviceId);
}
/**
* 启动心跳检测
*/
private startHeartbeat(deviceId: string): void {
try {
// 停止现有的心跳定时器
this.stopHeartbeat(deviceId);
const heartbeatInterval = CONSTANTS.DEFAULT_CONFIG.heartbeatInterval;
const heartbeatTimer = setInterval(async () => {
try {
await this.sendHeartbeat(deviceId);
} catch (error) {
logger.error('DeviceManager', `心跳发送失败: ${deviceId}`, error);
// 心跳失败,可能连接已断开
this.handleDeviceOffline({ deviceId });
}
}, heartbeatInterval) as unknown as number;
this.heartbeatTimers.set(deviceId, heartbeatTimer);
logger.debug('DeviceManager', `启动心跳检测: ${deviceId}, 间隔: ${heartbeatInterval}ms`);
} catch (error) {
logger.error('DeviceManager', `启动心跳检测失败: ${deviceId}`, error);
}
}
/**
* 停止心跳检测
*/
private stopHeartbeat(deviceId: string): void {
const timer = this.heartbeatTimers.get(deviceId);
if (timer) {
clearInterval(timer);
this.heartbeatTimers.delete(deviceId);
logger.debug('DeviceManager', `停止心跳检测: ${deviceId}`);
}
}
/**
* 发送心跳包
*/
private async sendHeartbeat(deviceId: string): Promise<void> {
try {
const deviceInfo = this.connectedDevices.get(deviceId);
if (!deviceInfo) {
throw new Error(`设备未连接: ${deviceId}`);
}
// 根据设备类型发送不同类型的心跳
if (deviceInfo.ipAddress) {
await this.networkManager.sendHeartbeat(deviceId);
} else {
// 其他类型设备的心跳实现
await this.delay(100);
}
logger.debug('DeviceManager', `心跳发送成功: ${deviceId}`);
} catch (error) {
logger.error('DeviceManager', `发送心跳失败: ${deviceId}`, error);
throw error;
}
}
/**
* 更新设备连接状态
*/
private updateDeviceConnectionStatus(deviceId: string, status: ConnectionStatus): void {
// 更新已连接设备状态
const connectedDevice = this.connectedDevices.get(deviceId);
if (connectedDevice) {
// 这里可以添加状态变更逻辑
}
// 更新发现设备状态
const discoveredDevice = this.discoveredDevices.get(deviceId);
if (discoveredDevice) {
// 创建设备状态对象
const deviceStatus = {
deviceId,
status,
powerState: false,
volume: 0,
channel: 0,
inputSource: '',
lastUpdate: Date.now()
};
// 通知状态变化
this.notifyConnectionStatusChange(deviceId, status);
}
}
/**
* 注册设备发现回调
*/
public registerDeviceFoundCallback(callback: DeviceFoundCallback): void {
this.deviceFoundCallbacks.push(callback);
logger.debug('DeviceManager', '注册设备发现回调');
}
/**
* 注销设备发现回调
*/
public unregisterDeviceFoundCallback(callback: DeviceFoundCallback): void {
const index = this.deviceFoundCallbacks.indexOf(callback);
if (index > -1) {
this.deviceFoundCallbacks.splice(index, 1);
logger.debug('DeviceManager', '注销设备发现回调');
}
}
/**
* 注册连接状态变化回调
*/
public registerConnectionStatusCallback(deviceId: string, callback: ConnectionStatusCallback): void {
if (!this.connectionCallbacks.has(deviceId)) {
this.connectionCallbacks.set(deviceId, []);
}
this.connectionCallbacks.get(deviceId)!.push(callback);
logger.debug('DeviceManager', `注册连接状态回调: ${deviceId}`);
}
/**
* 注销连接状态变化回调
*/
public unregisterConnectionStatusCallback(deviceId: string, callback: ConnectionStatusCallback): void {
const callbacks = this.connectionCallbacks.get(deviceId);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
logger.debug('DeviceManager', `注销连接状态回调: ${deviceId}`);
}
}
}
/**
* 通知设备发现
*/
private notifyDeviceFound(deviceInfo: DeviceInfo): void {
this.deviceFoundCallbacks.forEach(callback => {
try {
callback(deviceInfo);
} catch (error) {
logger.error('DeviceManager', '设备发现回调执行失败', error);
}
});
}
/**
* 通知连接状态变化
*/
private notifyConnectionStatusChange(deviceId: string, status: ConnectionStatus): void {
const callbacks = this.connectionCallbacks.get(deviceId);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(deviceId, status);
} catch (error) {
logger.error('DeviceManager', '连接状态回调执行失败', error);
}
});
}
}
/**
* 获取发现的设备列表
*/
public getDiscoveredDevices(): DeviceInfo[] {
return Array.from(this.discoveredDevices.values());
}
/**
* 获取已连接的设备列表
*/
public getConnectedDevices(): DeviceInfo[] {
return Array.from(this.connectedDevices.values());
}
/**
* 获取设备信息
*/
public getDeviceInfo(deviceId: string): DeviceInfo | undefined {
return this.discoveredDevices.get(deviceId) || this.connectedDevices.get(deviceId);
}
/**
* 检查设备是否已连接
*/
public isDeviceConnected(deviceId: string): boolean {
return this.connectedDevices.has(deviceId);
}
/**
* 获取设备连接状态
*/
public getDeviceConnectionStatus(deviceId: string): ConnectionStatus {
const connectedDevice = this.connectedDevices.get(deviceId);
if (connectedDevice) {
return ConnectionStatus.CONNECTED;
}
const discoveredDevice = this.discoveredDevices.get(deviceId);
// 这里简化处理,实际应该维护设备的详细状态
return discoveredDevice ? ConnectionStatus.DISCONNECTED : ConnectionStatus.DISCONNECTED;
}
/**
* 处理错误
*/
private handleError(type: ErrorType, message: string, details?: any): void {
const error: AppError = {
type,
message,
code: this.getErrorCode(type),
details,
timestamp: Date.now()
};
logger.error('DeviceManager', `设备管理器错误: ${message}`, error);
// 这里可以添加错误上报逻辑
// this.reportError(error);
}
/**
* 获取错误码
*/
private getErrorCode(type: ErrorType): number {
const errorCodes: Record<ErrorType, number> = {
[ErrorType.DEVICE_NOT_FOUND]: 1001,
[ErrorType.CONNECTION_FAILED]: 1002,
[ErrorType.COMMAND_TIMEOUT]: 1003,
[ErrorType.PROTOCOL_ERROR]: 1004,
[ErrorType.PERMISSION_DENIED]: 1005,
[ErrorType.NETWORK_UNAVAILABLE]: 1006,
[ErrorType.BLUETOOTH_DISABLED]: 1007
};
return errorCodes[type] || 9999;
}
/**
* 延迟函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* 销毁设备管理器
*/
public destroy(): void {
logger.info('DeviceManager', '销毁设备管理器');
// 停止设备发现
this.stopDeviceDiscovery();
// 断开所有连接
this.connectedDevices.forEach((_, deviceId) => {
this.disconnectDevice(deviceId);
});
// 清理定时器
this.heartbeatTimers.forEach((timer, deviceId) => {
clearInterval(timer);
});
this.heartbeatTimers.clear();
// 清理回调
this.deviceFoundCallbacks = [];
this.connectionCallbacks.clear();
this.isInitialized = false;
}
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)