1. 业务背景与功能架构

1.1 养老场景中的WiFi配网需求

在益康养老物联网系统中,门禁设备作为重要的安防终端,需要通过WiFi网络接入华为云IoT平台,实现远程监控、数据上报和指令下发。WiFi状态检查与密码下发功能是设备配网的核心环节,直接关系到设备的在线率和系统稳定性。

核心痛点与解决方案:

  • 痛点1:护理员多为非技术人员,设备配网流程复杂
  • 痛点2:养老院WiFi环境复杂,多热点切换频繁
  • 痛点3:设备离线后重新配网困难
  • 解决方案:一站式自动化配网流程,智能WiFi检测与密码安全下发

1.2 整体架构设计

数据与网络层

硬件与系统层

业务逻辑层

应用层

WiFi检查页面

WiFi选择页面

密码输入页面

配网执行页面

WiFi状态检查器

WiFi扫描管理器

密码加密处理器

蓝牙指令下发器

设备响应监听器

系统WiFi服务

系统扫描接口

加密算法库

蓝牙低功耗BLE

门禁设备硬件

华为云IoT平台

本地安全存储

2. WiFi状态检查模块实现

2.1 权限配置与依赖声明

// features/device/src/main/module.json5
{
  "module": {
    "name": "device",
    "type": "shared",
    "description": "$string:device_desc",
    "deviceTypes": ["phone", "tablet"],
    "requestPermissions": [
      {
        "name": "ohos.permission.GET_WIFI_INFO",
        "reason": "$string:wifi_info_reason",
        "usedScene": {
          "abilities": ["DeviceAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.SET_WIFI_INFO",
        "reason": "$string:set_wifi_reason",
        "usedScene": {
          "abilities": ["DeviceAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": ["DeviceAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.ACCESS_BLUETOOTH",
        "reason": "$string:bluetooth_reason",
        "usedScene": {
          "abilities": ["DeviceAbility"],
          "when": "always"
        }
      }
    ],
    "definePermissions": [],
    "abilities": [
      {
        "name": "DeviceAbility",
        "srcEntry": "./ets/deviceability/DeviceAbility.ets",
        "description": "$string:DeviceAbility_desc",
        "icon": "$media:icon",
        "label": "$string:DeviceAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "actions": [
              "action.system.home"
            ],
            "entities": [
              "entity.system.home"
            ]
          }
        ]
      }
    ]
  }
}

2.2 WiFi状态检查核心实现

// features/device/src/main/ets/wifi/WifiStatusChecker.ets
import { wifi } from '@ohos.wifi';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';
import { emitter } from '@ohos.events.emitter';

/**
 * WiFi状态检查器
 * 负责检查设备WiFi状态、扫描网络、评估信号质量
 */
export class WifiStatusChecker {
  private static instance: WifiStatusChecker;
  private isChecking: boolean = false;
  private checkInterval: number = 3000; // 检查间隔3秒
  private maxRetryCount: number = 3;
  
  // WiFi信号强度等级
  private static readonly SIGNAL_LEVELS = {
    EXCELLENT: { min: -50, max: 0, level: 4, color: '#4CAF50' },    // 极好
    GOOD: { min: -60, max: -51, level: 3, color: '#8BC34A' },       // 良好
    FAIR: { min: -70, max: -61, level: 2, color: '#FFC107' },       // 一般
    POOR: { min: -85, max: -71, level: 1, color: '#FF9800' },       // 差
    UNUSABLE: { min: -100, max: -86, level: 0, color: '#F44336' }   // 不可用
  };
  
  public static getInstance(): WifiStatusChecker {
    if (!WifiStatusChecker.instance) {
      WifiStatusChecker.instance = new WifiStatusChecker();
    }
    return WifiStatusChecker.instance;
  }
  
  /**
   * 检查WiFi基础状态
   */
  public async checkBasicStatus(): Promise<WifiBasicStatus> {
    try {
      Logger.info('WifiStatusChecker', '开始检查WiFi基础状态');
      
      // 1. 检查WiFi开关状态
      const isWifiActive = await wifi.isWifiActive();
      
      if (!isWifiActive) {
        return {
          isEnabled: false,
          isConnected: false,
          message: 'WiFi未开启,请先打开WiFi',
          level: 0,
          recommendation: 'enable_wifi'
        };
      }
      
      // 2. 检查连接状态
      const linkedInfo = await wifi.getLinkedInfo();
      
      if (!linkedInfo) {
        return {
          isEnabled: true,
          isConnected: false,
          message: 'WiFi已开启但未连接网络',
          level: 0,
          recommendation: 'connect_network'
        };
      }
      
      // 3. 分析连接质量
      const signalLevel = this.calculateSignalLevel(linkedInfo.rssi);
      const ssid = linkedInfo.ssid ? linkedInfo.ssid.replace(/"/g, '') : '未知网络';
      
      return {
        isEnabled: true,
        isConnected: true,
        ssid,
        bssid: linkedInfo.bssid,
        rssi: linkedInfo.rssi,
        frequency: linkedInfo.frequency,
        level: signalLevel.level,
        levelColor: signalLevel.color,
        ipAddress: linkedInfo.ipAddress,
        message: `已连接: ${ssid} (${signalLevel.level}/4)`,
        recommendation: signalLevel.level >= 2 ? 'ready' : 'improve_signal'
      };
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      Logger.error('WifiStatusChecker', `WiFi状态检查失败: ${err.code}, ${err.message}`);
      
      return {
        isEnabled: false,
        isConnected: false,
        message: `WiFi状态检查失败: ${err.message}`,
        level: 0,
        recommendation: 'check_permission'
      };
    }
  }
  
  /**
   * 持续监测WiFi状态(用于配网过程)
   */
  public startContinuousMonitoring(callback: (status: WifiMonitoringStatus) => void): void {
    if (this.isChecking) {
      Logger.warn('WifiStatusChecker', 'WiFi监测已在进行中');
      return;
    }
    
    this.isChecking = true;
    let checkCount = 0;
    
    const monitoringInterval = setInterval(async () => {
      if (!this.isChecking) {
        clearInterval(monitoringInterval);
        return;
      }
      
      checkCount++;
      
      try {
        const basicStatus = await this.checkBasicStatus();
        const scanResults = await this.scanNetworksQuick();
        
        const monitoringStatus: WifiMonitoringStatus = {
          ...basicStatus,
          scanResults,
          checkCount,
          timestamp: Date.now(),
          stability: this.calculateStability(checkCount, basicStatus)
        };
        
        // 触发事件
        emitter.emit({
          eventId: EventConstants.WIFI_STATUS_MONITORING
        }, { status: monitoringStatus });
        
        // 执行回调
        callback(monitoringStatus);
        
        // 达到最大检查次数后停止
        if (checkCount >= this.maxRetryCount) {
          this.stopContinuousMonitoring();
        }
        
      } catch (error) {
        Logger.error('WifiStatusChecker', `${checkCount}次监测失败: ${error.message}`);
      }
    }, this.checkInterval);
    
    // 存储interval ID以便清理
    (this as any)._monitoringInterval = monitoringInterval;
  }
  
  /**
   * 停止持续监测
   */
  public stopContinuousMonitoring(): void {
    this.isChecking = false;
    
    if ((this as any)._monitoringInterval) {
      clearInterval((this as any)._monitoringInterval);
      (this as any)._monitoringInterval = null;
    }
    
    Logger.info('WifiStatusChecker', 'WiFi持续监测已停止');
  }
  
  /**
   * 快速扫描周边网络(简化版本,用于实时监测)
   */
  private async scanNetworksQuick(): Promise<Array<WifiScanSimple>> {
    try {
      await wifi.scan();
      const scanResults = await wifi.getScanInfoList();
      
      return scanResults
        .slice(0, 5) // 只取前5个最强的信号
        .map(result => ({
          ssid: result.ssid ? result.ssid.replace(/"/g, '') : '未知',
          bssid: result.bssid,
          rssi: result.rssi,
          level: this.calculateSignalLevel(result.rssi).level,
          securityType: this.getSecurityTypeName(result.securityType),
          frequency: result.frequency,
          isCurrent: false // 在外部与当前连接对比后设置
        }));
        
    } catch (error) {
      Logger.warn('WifiStatusChecker', '快速扫描失败,返回空列表');
      return [];
    }
  }
  
  /**
   * 计算信号强度等级
   */
  private calculateSignalLevel(rssi: number): SignalLevel {
    const rssiValue = rssi || -100;
    
    for (const [key, range] of Object.entries(WifiStatusChecker.SIGNAL_LEVELS)) {
      if (rssiValue >= range.min && rssiValue <= range.max) {
        return {
          level: range.level,
          color: range.color,
          description: this.getSignalDescription(range.level)
        };
      }
    }
    
    // 默认返回最差等级
    const poorest = WifiStatusChecker.SIGNAL_LEVELS.UNUSABLE;
    return {
      level: poorest.level,
      color: poorest.color,
      description: this.getSignalDescription(poorest.level)
    };
  }
  
  /**
   * 计算连接稳定性
   */
  private calculateStability(checkCount: number, status: WifiBasicStatus): number {
    if (checkCount <= 1) {
      return 0; // 首次检查无法评估稳定性
    }
    
    // 简化稳定性计算:如果连续检查都连接良好,稳定性高
    // 实际应用中应基于历史数据计算
    return status.isConnected && status.level >= 2 ? 0.8 : 0.3;
  }
  
  /**
   * 获取安全类型名称
   */
  private getSecurityTypeName(type: number): string {
    const securityTypes: Record<number, string> = {
      0: '开放网络',
      1: 'WEP',
      2: 'PSK',
      3: 'EAP',
      4: 'SAE',
      5: 'OWE',
      6: 'SUITE_B'
    };
    
    return securityTypes[type] || '未知';
  }
  
  /**
   * 获取信号描述
   */
  private getSignalDescription(level: number): string {
    const descriptions = [
      '信号极弱,无法稳定连接',
      '信号较弱,连接可能不稳定',
      '信号一般,可以连接',
      '信号良好,连接稳定',
      '信号极佳,连接非常稳定'
    ];
    
    return descriptions[level] || descriptions[0];
  }
}

// 类型定义
export interface WifiBasicStatus {
  isEnabled: boolean;
  isConnected: boolean;
  ssid?: string;
  bssid?: string;
  rssi?: number;
  frequency?: number;
  level: number;
  levelColor?: string;
  ipAddress?: number;
  message: string;
  recommendation: 'enable_wifi' | 'connect_network' | 'improve_signal' | 'ready' | 'check_permission';
}

export interface WifiMonitoringStatus extends WifiBasicStatus {
  scanResults: Array<WifiScanSimple>;
  checkCount: number;
  timestamp: number;
  stability: number;
}

export interface WifiScanSimple {
  ssid: string;
  bssid: string;
  rssi: number;
  level: number;
  securityType: string;
  frequency: number;
  isCurrent: boolean;
}

export interface SignalLevel {
  level: number; // 0-4
  color: string;
  description: string;
}

2.3 WiFi状态检查页面实现

// features/device/src/main/ets/pages/WifiCheckPage.ets
import { WifiStatusChecker, WifiMonitoringStatus } from '../wifi/WifiStatusChecker';
import { Logger } from '@elderly/basic';
import { Router } from '@elderly/basic';

@Component
export struct WifiCheckPage {
  @State currentStatus: WifiMonitoringStatus = {
    isEnabled: false,
    isConnected: false,
    message: '正在检查WiFi状态...',
    level: 0,
    recommendation: 'check_permission',
    scanResults: [],
    checkCount: 0,
    timestamp: Date.now(),
    stability: 0
  };
  
  @State isLoading: boolean = true;
  @State checkProgress: number = 0;
  @State showDetails: boolean = false;
  
  private wifiChecker: WifiStatusChecker = WifiStatusChecker.getInstance();
  private progressInterval: number | null = null;
  
  aboutToAppear(): void {
    this.startWifiCheck();
  }
  
  aboutToDisappear(): void {
    this.wifiChecker.stopContinuousMonitoring();
    if (this.progressInterval) {
      clearInterval(this.progressInterval);
    }
  }
  
  /**
   * 开始WiFi检查
   */
  private async startWifiCheck(): Promise<void> {
    this.isLoading = true;
    this.checkProgress = 0;
    
    // 启动进度条动画
    this.startProgressAnimation();
    
    try {
      // 首次快速检查
      const initialStatus = await this.wifiChecker.checkBasicStatus();
      this.currentStatus = {
        ...this.currentStatus,
        ...initialStatus,
        checkCount: 1,
        timestamp: Date.now()
      };
      
      this.checkProgress = 50;
      
      // 开始持续监测
      this.wifiChecker.startContinuousMonitoring((status: WifiMonitoringStatus) => {
        this.currentStatus = status;
        
        // 根据监测结果更新进度
        if (status.isConnected && status.level >= 2) {
          this.checkProgress = Math.min(100, this.checkProgress + 20);
        }
        
        // 检查完成条件
        if (status.checkCount >= 3 || (status.isConnected && status.stability > 0.7)) {
          this.isLoading = false;
          this.wifiChecker.stopContinuousMonitoring();
          if (this.progressInterval) {
            clearInterval(this.progressInterval);
            this.progressInterval = null;
          }
        }
      });
      
    } catch (error) {
      Logger.error('WifiCheckPage', `WiFi检查启动失败: ${error.message}`);
      this.currentStatus.message = `检查失败: ${error.message}`;
      this.isLoading = false;
    }
  }
  
  /**
   * 启动进度条动画
   */
  private startProgressAnimation(): void {
    this.progressInterval = setInterval(() => {
      if (this.checkProgress < 90 && this.isLoading) {
        this.checkProgress += 5;
      }
    }, 500) as unknown as number;
  }
  
  /**
   * 重新检查
   */
  private recheck(): void {
    this.startWifiCheck();
  }
  
  /**
   * 前往WiFi设置
   */
  private goToWifiSettings(): void {
    // 鸿蒙系统中打开WiFi设置的方式
    // 实际应根据系统API调整
    Logger.info('WifiCheckPage', '跳转到系统WiFi设置');
    
    // 这里可以调用系统能力打开设置
    // import settings from '@ohos.settings';
    // settings.openWirelessSettings();
  }
  
  /**
   * 进入下一步(WiFi选择)
   */
  private goToNextStep(): void {
    if (this.currentStatus.isConnected && this.currentStatus.level >= 2) {
      Router.pushUrl({
        url: 'pages/WifiSelectPage',
        params: {
          currentSsid: this.currentStatus.ssid
        }
      });
    }
  }
  
  /**
   * 渲染状态指示器
   */
  @Builder
  private renderStatusIndicator() {
    Column({ space: 15 }) {
      // 状态图标
      Stack({ alignContent: Alignment.Center }) {
        // 背景圆环
        Circle({ width: 120, height: 120 })
          .fill(this.getStatusColor())
          .opacity(0.1)
        
        // 状态图标
        Image(this.getStatusIcon())
          .width(60)
          .height(60)
        
        // 进度环(加载时显示)
        if (this.isLoading) {
          Circle({ width: 130, height: 130 })
            .strokeWidth(4)
            .stroke(this.getStatusColor())
            .fill(Color.Transparent)
            .strokeDashArray([3, 5])
            .strokeDashOffset(360 * (1 - this.checkProgress / 100))
        }
      }
      
      // 状态文本
      Column({ space: 8 }) {
        Text(this.getStatusTitle())
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.Black)
        
        Text(this.currentStatus.message)
          .fontSize(14)
          .fontColor('#666666')
          .textAlign(TextAlign.Center)
          .maxLines(2)
      }
      .width('80%')
    }
    .width('100%')
    .padding(30)
  }
  
  /**
   * 渲染信号强度指示器
   */
  @Builder
  private renderSignalIndicator() {
    if (!this.currentStatus.isConnected) {
      return;
    }
    
    Column({ space: 10 }) {
      Text('信号强度')
        .fontSize(16)
        .fontColor(Color.Black)
      
      Row({ space: 4 }) {
        // 5个信号条
        ForEach(Array.from({ length: 5 }), (_, index: number) => {
          const isActive = index < this.currentStatus.level;
          const height = 10 + index * 8; // 递增高度
          
          Rectangle()
            .width(10)
            .height(height)
            .fill(isActive ? this.currentStatus.levelColor || '#4CAF50' : '#E0E0E0')
            .borderRadius(2)
        })
      }
      .height(50)
      .justifyContent(FlexAlign.End)
      .alignItems(VerticalAlign.Bottom)
      
      Text(`${this.currentStatus.level}/4 - ${this.getSignalDescription(this.currentStatus.level)}`)
        .fontSize(12)
        .fontColor('#666666')
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#F8F9FA')
    .borderRadius(12)
    .margin({ top: 10, bottom: 10 })
  }
  
  /**
   * 渲染网络详情
   */
  @Builder
  private renderNetworkDetails() {
    if (!this.currentStatus.isConnected || !this.showDetails) {
      return;
    }
    
    Column({ space: 12 }) {
      Text('网络详情')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .fontColor(Color.Black)
        .width('100%')
      
      this.renderDetailItem('网络名称', this.currentStatus.ssid || '未知')
      this.renderDetailItem('BSSID', this.currentStatus.bssid || '未知')
      this.renderDetailItem('IP地址', this.formatIpAddress(this.currentStatus.ipAddress))
      this.renderDetailItem('频段', this.getFrequencyBand(this.currentStatus.frequency))
      this.renderDetailItem('稳定性', `${Math.round(this.currentStatus.stability * 100)}%`)
    }
    .width('100%')
    .padding(20)
    .backgroundColor(Color.White)
    .border({ width: 1, color: '#EEEEEE' })
    .borderRadius(12)
    .margin({ top: 10 })
  }
  
  /**
   * 渲染详情项
   */
  @Builder
  private renderDetailItem(label: string, value: string) {
    Row({ space: 15 }) {
      Text(label)
        .fontSize(14)
        .fontColor('#666666')
        .width(80)
      
      Text(value)
        .fontSize(14)
        .fontColor(Color.Black)
        .layoutWeight(1)
        .textAlign(TextAlign.End)
    }
    .width('100%')
  }
  
  /**
   * 渲染操作按钮
   */
  @Builder
  private renderActionButtons() {
    Column({ space: 15 }) {
      if (this.currentStatus.isConnected && this.currentStatus.level >= 2) {
        Button('网络良好,继续配置设备')
          .width('90%')
          .height(50)
          .backgroundColor(Color.Blue)
          .fontColor(Color.White)
          .fontSize(16)
          .enabled(!this.isLoading)
          .onClick(() => this.goToNextStep())
      } else {
        Button('前往WiFi设置')
          .width('90%')
          .height(50)
          .backgroundColor(Color.Blue)
          .fontColor(Color.White)
          .fontSize(16)
          .enabled(!this.isLoading)
          .onClick(() => this.goToWifiSettings())
      }
      
      Button(this.isLoading ? '检查中...' : '重新检查')
        .width('90%')
        .height(45)
        .backgroundColor('#F0F0F0')
        .fontColor(Color.Black)
        .fontSize(16)
        .enabled(!this.isLoading)
        .onClick(() => this.recheck())
    }
    .width('100%')
    .padding(20)
  }
  
  /**
   * 获取状态颜色
   */
  private getStatusColor(): Color {
    if (this.isLoading) return Color.Blue;
    
    if (!this.currentStatus.isEnabled) return Color.Gray;
    if (!this.currentStatus.isConnected) return Color.Orange;
    
    switch (this.currentStatus.level) {
      case 0: case 1: return Color.Red;
      case 2: return Color.Orange;
      case 3: return '#8BC34A';
      case 4: return '#4CAF50';
      default: return Color.Gray;
    }
  }
  
  /**
   * 获取状态图标
   */
  private getStatusIcon(): Resource {
    if (this.isLoading) return $r('app.media.ic_wifi_loading');
    
    if (!this.currentStatus.isEnabled) return $r('app.media.ic_wifi_off');
    if (!this.currentStatus.isConnected) return $r('app.media.ic_wifi_disconnected');
    
    switch (this.currentStatus.level) {
      case 0: return $r('app.media.ic_wifi_0');
      case 1: return $r('app.media.ic_wifi_1');
      case 2: return $r('app.media.ic_wifi_2');
      case 3: return $r('app.media.ic_wifi_3');
      case 4: return $r('app.media.ic_wifi_4');
      default: return $r('app.media.ic_wifi_unknown');
    }
  }
  
  /**
   * 获取状态标题
   */
  private getStatusTitle(): string {
    if (this.isLoading) return '检查WiFi状态中...';
    
    if (!this.currentStatus.isEnabled) return 'WiFi未开启';
    if (!this.currentStatus.isConnected) return '未连接网络';
    
    switch (this.currentStatus.level) {
      case 0: case 1: return '信号太弱';
      case 2: return '信号一般';
      case 3: return '信号良好';
      case 4: return '信号极佳';
      default: return '未知状态';
    }
  }
  
  /**
   * 获取信号描述
   */
  private getSignalDescription(level: number): string {
    const descriptions = [
      '无法连接',
      '非常弱',
      '一般',
      '良好',
      '极佳'
    ];
    return descriptions[level] || '未知';
  }
  
  /**
   * 格式化IP地址
   */
  private formatIpAddress(ip?: number): string {
    if (!ip) return '未知';
    
    return [
      (ip >> 24) & 0xFF,
      (ip >> 16) & 0xFF,
      (ip >> 8) & 0xFF,
      ip & 0xFF
    ].join('.');
  }
  
  /**
   * 获取频段信息
   */
  private getFrequencyBand(frequency?: number): string {
    if (!frequency) return '未知';
    
    if (frequency >= 2412 && frequency <= 2484) {
      return `2.4GHz (${frequency}MHz)`;
    } else if (frequency >= 5170 && frequency <= 5825) {
      return `5GHz (${frequency}MHz)`;
    } else {
      return `${frequency}MHz`;
    }
  }
  
  build() {
    Column({ space: 0 }) {
      // 顶部标题
      Text('WiFi状态检查')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.Black)
        .width('100%')
        .textAlign(TextAlign.Center)
        .padding({ top: 20, bottom: 20 })
        .backgroundColor(Color.White)
      
      Scroll() {
        Column({ space: 20 }) {
          // 状态指示器
          this.renderStatusIndicator()
          
          // 信号强度指示
          if (this.currentStatus.isConnected) {
            this.renderSignalIndicator()
          }
          
          // 详情切换按钮
          if (this.currentStatus.isConnected) {
            Row() {
              Text(this.showDetails ? '隐藏详情' : '显示详情')
                .fontSize(14)
                .fontColor(Color.Blue)
              
              Image($r('app.media.ic_arrow_down'))
                .width(16)
                .height(16)
                .rotate({ angle: this.showDetails ? 180 : 0 })
            }
            .width('100%')
            .justifyContent(FlexAlign.Center)
            .onClick(() => {
              this.showDetails = !this.showDetails;
            })
            .padding(10)
          }
          
          // 网络详情
          if (this.showDetails) {
            this.renderNetworkDetails()
          }
          
          // 操作按钮
          this.renderActionButtons()
          
          // 帮助提示
          Column({ space: 8 }) {
            Text('温馨提示')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
              .fontColor('#666666')
            
            Text('• 确保设备靠近路由器,信号强度达到3格以上')
              .fontSize(12)
              .fontColor('#999999')
            
            Text('• 养老院公共WiFi可能需要网页认证')
              .fontSize(12)
              .fontColor('#999999')
            
            Text('• 如遇连接问题,请联系网络管理员')
              .fontSize(12)
              .fontColor('#999999')
          }
          .width('90%')
          .padding(15)
          .backgroundColor('#F8F9FA')
          .borderRadius(10)
          .margin({ top: 20, bottom: 40 })
        }
        .width('100%')
        .padding(20)
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F7FA')
  }
}

3. WiFi密码下发模块实现

3.1 密码安全处理与加密

// features/device/src/main/ets/wifi/PasswordSecurityManager.ets
import { cryptoFramework } from '@ohos.security.cryptoFramework';
import { util } from '@ohos.util';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';

/**
 * 密码安全管理器
 * 负责密码的加密、安全存储和安全传输
 */
export class PasswordSecurityManager {
  private static instance: PasswordSecurityManager;
  private static readonly AES_ALGORITHM = 'AES256|GCM|PKCS7';
  private static readonly AES_KEY_SIZE = 256; // 位
  private static readonly ITERATION_COUNT = 10000;
  private static readonly SALT_SIZE = 16; // 字节
  
  // 设备特定的预共享密钥(实际应从安全芯片或安全存储获取)
  private devicePreSharedKey: string = 'ELDERLY_CARE_2024_SECURE_KEY';
  
  public static getInstance(): PasswordSecurityManager {
    if (!PasswordSecurityManager.instance) {
      PasswordSecurityManager.instance = new PasswordSecurityManager();
    }
    return PasswordSecurityManager.instance;
  }
  
  /**
   * 加密WiFi密码(用于安全存储和传输)
   */
  public async encryptPassword(password: string, deviceId: string): Promise<EncryptedPassword> {
    try {
      Logger.info('PasswordSecurityManager', `开始加密密码,设备ID: ${deviceId}`);
      
      // 1. 生成随机的盐值
      const salt = this.generateRandomBytes(PasswordSecurityManager.SALT_SIZE);
      
      // 2. 从预共享密钥和设备ID派生加密密钥
      const encryptionKey = await this.deriveEncryptionKey(
        this.devicePreSharedKey,
        deviceId,
        salt
      );
      
      // 3. 加密密码
      const encryptedData = await this.aesGcmEncrypt(password, encryptionKey, salt);
      
      // 4. 计算完整性校验值
      const integrityCheck = await this.calculateIntegrityCheck(password, deviceId);
      
      const result: EncryptedPassword = {
        encryptedData: encryptedData.cipherText,
        iv: encryptedData.iv,
        salt: salt,
        integrityCheck,
        deviceId,
        timestamp: Date.now(),
        algorithm: 'AES256-GCM-PKCS7',
        version: '1.0'
      };
      
      Logger.info('PasswordSecurityManager', '密码加密成功');
      return result;
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      Logger.error('PasswordSecurityManager', `密码加密失败: ${err.code}, ${err.message}`);
      throw new Error(`密码加密失败: ${err.message}`);
    }
  }
  
  /**
   * 生成蓝牙传输的密码指令包
   */
  public async generatePasswordPacket(
    ssid: string, 
    password: string, 
    deviceId: string
  ): Promise<PasswordPacket> {
    try {
      // 1. 加密密码
      const encryptedPassword = await this.encryptPassword(password, deviceId);
      
      // 2. 构建指令数据结构
      const packetData = this.buildPacketData(ssid, encryptedPassword);
      
      // 3. 计算CRC校验
      const crc32 = this.calculateCrc32(packetData);
      
      // 4. 构建完整指令包
      const packet: PasswordPacket = {
        header: 0xAA55, // 包头
        version: 0x01,  // 协议版本
        command: 0x10,  // WiFi配置指令
        deviceId,
        ssid,
        dataLength: packetData.length,
        data: packetData,
        crc32,
        footer: 0x55AA  // 包尾
      };
      
      // 5. 转换为Uint8Array用于蓝牙传输
      const packetBytes = this.packetToUint8Array(packet);
      
      return {
        ...packet,
        rawBytes: packetBytes,
        hexString: this.bytesToHex(packetBytes)
      };
      
    } catch (error) {
      Logger.error('PasswordSecurityManager', `生成密码包失败: ${error.message}`);
      throw error;
    }
  }
  
  /**
   * AES-GCM加密
   */
  private async aesGcmEncrypt(
    plainText: string, 
    key: Uint8Array, 
    salt: Uint8Array
  ): Promise<AesEncryptionResult> {
    try {
      // 创建对称密钥生成器
      const symKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
      
      // 创建加密参数
      const gcmParams = cryptoFramework.createGcmParamsSpec({
        iv: salt.slice(0, 12), // GCM推荐12字节IV
        aad: new Uint8Array(), // 附加认证数据
        authTagLength: 128 // 认证标签长度(位)
      });
      
      // 生成对称密钥
      const symKey = await symKeyGenerator.convertKey({ data: key });
      
      // 创建加密器
      const cipher = cryptoFramework.createCipher('AES256|GCM|PKCS7');
      await cipher.init(cryptoFramework.CryptoMode.ENCRYPT_MODE, symKey, gcmParams);
      
      // 加密数据
      const textEncoder = new util.TextEncoder();
      const plainData = { data: textEncoder.encodeInto(plainText) };
      const encryptedData = await cipher.doFinal(plainData);
      
      return {
        cipherText: Array.from(new Uint8Array(encryptedData.data)),
        iv: Array.from(salt.slice(0, 12))
      };
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      throw new Error(`AES加密失败: ${err.message}`);
    }
  }
  
  /**
   * 派生加密密钥(使用PBKDF2)
   */
  private async deriveEncryptionKey(
    preSharedKey: string, 
    deviceId: string, 
    salt: Uint8Array
  ): Promise<Uint8Array> {
    try {
      // 组合密钥材料
      const keyMaterial = `${preSharedKey}:${deviceId}:${Date.now()}`;
      const textEncoder = new util.TextEncoder();
      const materialBytes = textEncoder.encodeInto(keyMaterial);
      
      // 创建PBKDF2参数
      const pbkdf2Params: cryptoFramework.PBKDF2ParamsSpec = {
        algName: 'SHA256',
        password: { data: materialBytes },
        salt: { data: salt },
        iterations: PasswordSecurityManager.ITERATION_COUNT,
        keySize: PasswordSecurityManager.AES_KEY_SIZE / 8 // 字节数
      };
      
      // 派生密钥
      const pbkdf2 = cryptoFramework.createPBKDF2();
      const derivedKey = await pbkdf2.generateKey(pbkdf2Params);
      
      return new Uint8Array(derivedKey.data);
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      throw new Error(`密钥派生失败: ${err.message}`);
    }
  }
  
  /**
   * 计算完整性校验值
   */
  private async calculateIntegrityCheck(password: string, deviceId: string): Promise<string> {
    try {
      const textEncoder = new util.TextEncoder();
      const data = textEncoder.encodeInto(`${password}:${deviceId}:${Date.now()}`);
      
      const sha256 = cryptoFramework.createHash('SHA256');
      await sha256.update({ data });
      const hash = await sha256.digest();
      
      // 取前8字节作为校验值
      const hashBytes = new Uint8Array(hash.data);
      const checkBytes = hashBytes.slice(0, 8);
      
      return this.bytesToHex(checkBytes);
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      throw new Error(`完整性校验计算失败: ${err.message}`);
    }
  }
  
  /**
   * 构建指令包数据
   */
  private buildPacketData(ssid: string, encryptedPassword: EncryptedPassword): number[] {
    const data: number[] = [];
    
    // SSID长度 + SSID内容
    const ssidBytes = new TextEncoder().encode(ssid);
    data.push(ssidBytes.length);
    data.push(...Array.from(ssidBytes));
    
    // 加密数据长度 + 加密数据
    data.push(encryptedPassword.encryptedData.length);
    data.push(...encryptedPassword.encryptedData);
    
    // IV长度 + IV
    data.push(encryptedPassword.iv.length);
    data.push(...encryptedPassword.iv);
    
    // 盐值长度 + 盐值
    data.push(encryptedPassword.salt.length);
    data.push(...encryptedPassword.salt);
    
    // 完整性校验(转换为字节)
    const integrityBytes = this.hexToBytes(encryptedPassword.integrityCheck);
    data.push(integrityBytes.length);
    data.push(...Array.from(integrityBytes));
    
    return data;
  }
  
  /**
   * 计算CRC32校验
   */
  private calculateCrc32(data: number[]): number {
    // 简化的CRC32计算(实际应使用标准CRC32算法)
    let crc = 0xFFFFFFFF;
    
    for (const byte of data) {
      crc ^= byte;
      for (let i = 0; i < 8; i++) {
        crc = (crc >>> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
      }
    }
    
    return crc ^ 0xFFFFFFFF;
  }
  
  /**
   * 将指令包转换为Uint8Array
   */
  private packetToUint8Array(packet: PasswordPacket): Uint8Array {
    const bufferSize = 2 + 1 + 1 + 32 + 2 + 32 + 2 + packet.dataLength + 4 + 2;
    const buffer = new ArrayBuffer(bufferSize);
    const view = new DataView(buffer);
    let offset = 0;
    
    // 包头 (2字节)
    view.setUint16(offset, packet.header, true);
    offset += 2;
    
    // 版本 (1字节)
    view.setUint8(offset, packet.version);
    offset += 1;
    
    // 指令 (1字节)
    view.setUint8(offset, packet.command);
    offset += 1;
    
    // 设备ID (32字节,固定长度)
    const deviceIdBytes = new TextEncoder().encode(packet.deviceId.padEnd(32, '\0'));
    for (let i = 0; i < 32; i++) {
      view.setUint8(offset + i, deviceIdBytes[i]);
    }
    offset += 32;
    
    // SSID长度 (2字节)
    const ssidBytes = new TextEncoder().encode(packet.ssid);
    view.setUint16(offset, ssidBytes.length, true);
    offset += 2;
    
    // SSID内容
    for (let i = 0; i < ssidBytes.length; i++) {
      view.setUint8(offset + i, ssidBytes[i]);
    }
    offset += ssidBytes.length;
    
    // 数据长度 (2字节)
    view.setUint16(offset, packet.dataLength, true);
    offset += 2;
    
    // 数据内容
    for (let i = 0; i < packet.data.length; i++) {
      view.setUint8(offset + i, packet.data[i]);
    }
    offset += packet.data.length;
    
    // CRC32校验 (4字节)
    view.setUint32(offset, packet.crc32, true);
    offset += 4;
    
    // 包尾 (2字节)
    view.setUint16(offset, packet.footer, true);
    
    return new Uint8Array(buffer);
  }
  
  /**
   * 生成随机字节
   */
  private generateRandomBytes(size: number): Uint8Array {
    const bytes = new Uint8Array(size);
    for (let i = 0; i < size; i++) {
      bytes[i] = Math.floor(Math.random() * 256);
    }
    return bytes;
  }
  
  /**
   * 字节数组转十六进制字符串
   */
  private bytesToHex(bytes: Uint8Array): string {
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
  
  /**
   * 十六进制字符串转字节数组
   */
  private hexToBytes(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < hex.length; i += 2) {
      bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
    }
    return bytes;
  }
}

// 类型定义
export interface EncryptedPassword {
  encryptedData: number[];
  iv: number[];
  salt: Uint8Array;
  integrityCheck: string;
  deviceId: string;
  timestamp: number;
  algorithm: string;
  version: string;
}

export interface PasswordPacket {
  header: number;
  version: number;
  command: number;
  deviceId: string;
  ssid: string;
  dataLength: number;
  data: number[];
  crc32: number;
  footer: number;
  rawBytes?: Uint8Array;
  hexString?: string;
}

export interface AesEncryptionResult {
  cipherText: number[];
  iv: number[];
}

3.2 密码下发执行器

// features/device/src/main/ets/wifi/PasswordDeliveryExecutor.ets
import { ble, connection } from '@ohos.bluetooth';
import { BusinessError } from '@ohos.base';
import { Logger } from '@elderly/basic';
import { PasswordSecurityManager, PasswordPacket } from './PasswordSecurityManager';
import { AccessControlManager } from '../bluetooth/AccessControlManager';

/**
 * 密码下发执行器
 * 负责通过蓝牙安全下发WiFi密码到门禁设备
 */
export class PasswordDeliveryExecutor {
  private static instance: PasswordDeliveryExecutor;
  private securityManager: PasswordSecurityManager;
  private accessControlManager: AccessControlManager;
  private isDelivering: boolean = false;
  private currentDeliveryId: string | null = null;
  
  // 蓝牙服务UUID(与门禁设备约定)
  private static readonly SERVICE_UUID: string = '0000AE30-0000-1000-8000-00805F9B34FB';
  // 写入特征值UUID
  private static readonly WRITE_CHAR_UUID: string = '0000AE10-0000-1000-8000-00805F9B34FB';
  // 通知特征值UUID(用于接收设备响应)
  private static readonly NOTIFY_CHAR_UUID: string = '0000AE04-0000-1000-8000-00805F9B34FB';
  
  // 下发状态常量
  private static readonly DELIVERY_STATUS = {
    PENDING: 'pending',
    SENDING: 'sending',
    DEVICE_ACK: 'device_ack',
    CONNECTING_WIFI: 'connecting_wifi',
    SUCCESS: 'success',
    FAILED: 'failed',
    TIMEOUT: 'timeout'
  };
  
  public static getInstance(): PasswordDeliveryExecutor {
    if (!PasswordDeliveryExecutor.instance) {
      PasswordDeliveryExecutor.instance = new PasswordDeliveryExecutor();
    }
    return PasswordDeliveryExecutor.instance;
  }
  
  private constructor() {
    this.securityManager = PasswordSecurityManager.getInstance();
    this.accessControlManager = AccessControlManager.getInstance();
  }
  
  /**
   * 执行密码下发流程
   */
  public async executeDelivery(
    ssid: string,
    password: string,
    deviceId: string,
    onProgress?: (progress: DeliveryProgress) => void
  ): Promise<DeliveryResult> {
    if (this.isDelivering) {
      return {
        success: false,
        message: '已有下发任务在进行中',
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED
      };
    }
    
    this.isDelivering = true;
    this.currentDeliveryId = `delivery_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    const startTime = Date.now();
    const timeoutDuration = 60000; // 60秒超时
    
    try {
      Logger.info('PasswordDeliveryExecutor', `开始密码下发,设备: ${deviceId}, SSID: ${ssid}`);
      
      // 1. 更新进度:准备中
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.PENDING,
        progress: 10,
        message: '准备下发数据...'
      });
      
      // 2. 生成加密的密码包
      const passwordPacket = await this.securityManager.generatePasswordPacket(ssid, password, deviceId);
      
      // 3. 更新进度:连接设备
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.PENDING,
        progress: 20,
        message: '连接门禁设备...'
      });
      
      // 4. 确保设备已连接
      const isConnected = await this.ensureDeviceConnected(deviceId);
      if (!isConnected) {
        throw new Error('设备连接失败');
      }
      
      // 5. 更新进度:下发数据
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.SENDING,
        progress: 40,
        message: '下发WiFi配置...'
      });
      
      // 6. 分块发送密码包(避免超过MTU限制)
      const sendResult = await this.sendPasswordPacket(deviceId, passwordPacket, onProgress);
      
      if (!sendResult.success) {
        throw new Error(`密码包发送失败: ${sendResult.message}`);
      }
      
      // 7. 等待设备响应
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.DEVICE_ACK,
        progress: 70,
        message: '等待设备确认...'
      });
      
      const deviceResponse = await this.waitForDeviceResponse(deviceId, 10000); // 10秒等待
      
      if (!deviceResponse.success) {
        throw new Error(`设备响应超时或失败: ${deviceResponse.message}`);
      }
      
      // 8. 更新进度:设备连接WiFi
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.CONNECTING_WIFI,
        progress: 85,
        message: '设备正在连接WiFi...'
      });
      
      // 9. 等待设备WiFi连接成功(通过通知监听)
      const wifiResult = await this.waitForWifiConnection(deviceId, 30000); // 30秒等待
      
      if (!wifiResult.success) {
        throw new Error(`设备WiFi连接失败: ${wifiResult.message}`);
      }
      
      // 10. 更新进度:完成
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.SUCCESS,
        progress: 100,
        message: 'WiFi配置成功!',
        details: {
          ssid,
          deviceIp: wifiResult.ipAddress,
          connectionTime: Date.now() - startTime
        }
      });
      
      Logger.info('PasswordDeliveryExecutor', `密码下发成功,总耗时: ${Date.now() - startTime}ms`);
      
      return {
        success: true,
        message: 'WiFi密码下发成功',
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.SUCCESS,
        deliveryId: this.currentDeliveryId,
        timestamp: Date.now(),
        details: {
          ssid,
          deviceIp: wifiResult.ipAddress,
          duration: Date.now() - startTime
        }
      };
      
    } catch (error) {
      Logger.error('PasswordDeliveryExecutor', `密码下发失败: ${error.message}`);
      
      this.updateProgress(onProgress, {
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED,
        progress: 0,
        message: `下发失败: ${error.message}`
      });
      
      return {
        success: false,
        message: error.message,
        status: PasswordDeliveryExecutor.DELIVERY_STATUS.FAILED,
        deliveryId: this.currentDeliveryId,
        timestamp: Date.now()
      };
      
    } finally {
      this.isDelivering = false;
      this.currentDeliveryId = null;
    }
  }
  
  /**
   * 确保设备已连接
   */
  private async ensureDeviceConnected(deviceId: string): Promise<boolean> {
    try {
      // 检查是否已连接
      const devices = await connection.getConnectedDevices();
      const isConnected = devices.some(device => device.deviceId === deviceId);
      
      if (isConnected) {
        return true;
      }
      
      // 重新连接
      Logger.info('PasswordDeliveryExecutor', `重新连接设备: ${deviceId}`);
      return await this.accessControlManager.connectDevice(deviceId);
      
    } catch (error) {
      Logger.error('PasswordDeliveryExecutor', `设备连接检查失败: ${error.message}`);
      return false;
    }
  }
  
  /**
   * 分块发送密码包
   */
  private async sendPasswordPacket(
    deviceId: string,
    packet: PasswordPacket,
    onProgress?: (progress: DeliveryProgress) => void
  ): Promise<SendResult> {
    try {
      if (!packet.rawBytes) {
        throw new Error('密码包数据为空');
      }
      
      const mtu = await this.getBleMtu(deviceId);
      const chunkSize = Math.min(mtu - 3, 20); // 预留3字节ATT头,最大20字节
      const totalChunks = Math.ceil(packet.rawBytes.length / chunkSize);
      
      Logger.debug('PasswordDeliveryExecutor', `MTU: ${mtu}, 分块大小: ${chunkSize}, 总块数: ${totalChunks}`);
      
      // 发送起始标记
      await this.sendPacketStartMarker(deviceId);
      
      // 分块发送
      for (let i = 0; i < totalChunks; i++) {
        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, packet.rawBytes.length);
        const chunk = packet.rawBytes.slice(start, end);
        
        // 添加块序号和总块数
        const chunkWithHeader = new Uint8Array(chunk.length + 2);
        chunkWithHeader[0] = i; // 块序号
        chunkWithHeader[1] = totalChunks; // 总块数
        chunkWithHeader.set(chunk, 2);
        
        await this.writeCharacteristic(deviceId, chunkWithHeader);
        
        // 更新进度
        const chunkProgress = 40 + Math.floor((i + 1) / totalChunks * 30); // 40% ~ 70%
        this.updateProgress(onProgress, {
          status: PasswordDeliveryExecutor.DELIVERY_STATUS.SENDING,
          progress: chunkProgress,
          message: `下发配置中... (${i + 1}/${totalChunks})`
        });
        
        // 小块延迟,避免发送过快
        if (i < totalChunks - 1) {
          await this.delay(50);
        }
      }
      
      // 发送结束标记
      await this.sendPacketEndMarker(deviceId);
      
      return {
        success: true,
        message: '密码包发送完成',
        chunksSent: totalChunks
      };
      
    } catch (error) {
      Logger.error('PasswordDeliveryExecutor', `密码包发送失败: ${error.message}`);
      return {
        success: false,
        message: `发送失败: ${error.message}`
      };
    }
  }
  
  /**
   * 发送数据包起始标记
   */
  private async sendPacketStartMarker(deviceId: string): Promise<void> {
    const startMarker = new Uint8Array([0xAA, 0x01]); // 起始标记
    await this.writeCharacteristic(deviceId, startMarker);
    await this.delay(100);
  }
  
  /**
   * 发送数据包结束标记
   */
  private async sendPacketEndMarker(deviceId: string): Promise<void> {
    const endMarker = new Uint8Array([0x55, 0x01]); // 结束标记
    await this.writeCharacteristic(deviceId, endMarker);
    await this.delay(100);
  }
  
  /**
   * 写入特征值
   */
  private async writeCharacteristic(deviceId: string, data: Uint8Array): Promise<void> {
    try {
      await ble.writeBLECharacteristicValue(deviceId, {
        serviceUuid: PasswordDeliveryExecutor.SERVICE_UUID,
        characteristicUuid: PasswordDeliveryExecutor.WRITE_CHAR_UUID,
        value: data.buffer
      });
      
      Logger.debug('PasswordDeliveryExecutor', `写入特征值成功,长度: ${data.length}`);
      
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      throw new Error(`写入特征值失败: ${err.code}, ${err.message}`);
    }
  }
  
  /**
   * 获取BLE MTU
   */
  private async getBleMtu(deviceId: string): Promise<number> {
    try {
      const mtu = await ble.getBLEMtu(deviceId);
      return mtu > 0 ? mtu : 23; // 默认23字节
    } catch (error) {
      Logger.warn('PasswordDeliveryExecutor', `获取MTU失败,使用默认值: ${error.message}`);
      return 23;
    }
  }
  
  /**
   * 等待设备响应
   */
  private waitForDeviceResponse(deviceId: string, timeout: number): Promise<DeviceResponse> {
    return new Promise((resolve) => {
      let responded = false;
      const startTime = Date.now();
      
      // 监听设备响应
      const responseHandler = (data: any) => {
        if (data.deviceId === deviceId && data.type === 'password_ack') {
          responded = true;
          emitter.off(responseHandler);
          
          resolve({
            success: true,
            message: '设备确认收到密码',
            ackCode: data.ackCode
          });
        }
      };
      
      emitter.on(EventConstants.DEVICE_RESPONSE, responseHandler);
      
      // 超时检查
      const checkTimeout = () => {
        if (!responded && (Date.now() - startTime) > timeout) {
          emitter.off(responseHandler);
          resolve({
            success: false,
            message: '设备响应超时'
          });
        } else if (!responded) {
          setTimeout(checkTimeout, 100);
        }
      };
      
      checkTimeout();
    });
  }
  
  /**
   * 等待设备WiFi连接
   */
  private waitForWifiConnection(deviceId: string, timeout: number): Promise<WifiConnectionResult> {
    return new Promise((resolve) => {
      let connected = false;
      const startTime = Date.now();
      
      // 监听WiFi连接状态
      const connectionHandler = (data: any) => {
        if (data.deviceId === deviceId && data.status === 'wifi_connected') {
          connected = true;
          emitter.off(connectionHandler);
          
          resolve({
            success: true,
            message: '设备WiFi连接成功',
            ipAddress: data.ipAddress,
            signalStrength: data.signalStrength
          });
        }
      };
      
      emitter.on(EventConstants.WIFI_CONNECTION_STATUS, connectionHandler);
      
      // 超时检查
      const checkTimeout = () => {
        if (!connected && (Date.now() - startTime) > timeout) {
          emitter.off(connectionHandler);
          resolve({
            success: false,
            message: '设备WiFi连接超时'
          });
        } else if (!connected) {
          setTimeout(checkTimeout, 100);
        }
      };
      
      checkTimeout();
    });
  }
  
  /**
   * 更新下发进度
   */
  private updateProgress(
    onProgress: ((progress: DeliveryProgress) => void) | undefined,
    progress: DeliveryProgress
  ): void {
    if (onProgress) {
      onProgress(progress);
    }
    
    // 同时触发全局事件
    emitter.emit({
      eventId: EventConstants.PASSWORD_DELIVERY_PROGRESS
    }, { progress });
  }
  
  /**
   * 延迟函数
   */
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  /**
   * 取消当前下发任务
   */
  public cancelDelivery(): void {
    if (this.isDelivering) {
      this.isDelivering = false;
      Logger.info('PasswordDeliveryExecutor', '密码下发任务已取消');
    }
  }
}

// 类型定义
export interface DeliveryProgress {
  status: string;
  progress: number; // 0-100
  message: string;
  details?: Record<string, any>;
}

export interface DeliveryResult {
  success: boolean;
  message: string;
  status: string;
  deliveryId?: string;
  timestamp: number;
  details?: Record<string, any>;
}

export interface SendResult {
  success: boolean;
  message: string;
  chunksSent?: number;
}

export interface DeviceResponse {
  success: boolean;
  message: string;
  ackCode?: number;
}

export interface WifiConnectionResult {
  success: boolean;
  message: string;
  ipAddress?: string;
  signalStrength?: number;
}

4. 完整配网流程整合

4.1 配网流程状态机

信号强度≥3

需要优化

重新检查

验证通过

验证失败

重新输入

连接成功

连接超时

重试

开始下发

收到ACK

超时/错误

重试

设备开始连接

获取IP

超时

配网完成

WiFi检查

WiFi良好

WiFi设置

选择网络

输入密码

验证密码

密码有效

密码无效

连接设备

下发密码

连接失败

等待响应

设备确认

下发失败

连接WiFi

连接成功

4.2 流程执行控制器

// features/device/src/main/ets/wifi/NetworkingFlowController.ets
import { WifiStatusChecker } from './WifiStatusChecker';
import { PasswordDeliveryExecutor } from './PasswordDeliveryExecutor';
import { Logger } from '@elderly/basic';
import { emitter } from '@ohos.events.emitter';

/**
 * 配网流程控制器
 * 协调WiFi检查、密码下发、状态监控等各个模块
 */
export class NetworkingFlowController {
  private static instance: NetworkingFlowController;
  private wifiChecker: WifiStatusChecker;
  private passwordExecutor: PasswordDeliveryExecutor;
  private currentState: NetworkingState = NetworkingState.IDLE;
  private currentDeviceId: string | null = null;
  private retryCount: number = 0;
  private maxRetries: number = 3;
  
  // 配网状态定义
  private static readonly NETWORKING_STATE = {
    IDLE: 'idle',
    CHECKING_WIFI: 'checking_wifi',
    SELECTING_NETWORK: 'selecting_network',
    ENTERING_PASSWORD: 'entering_password',
    CONNECTING_DEVICE: 'connecting_device',
    DELIVERING_PASSWORD: 'delivering_password',
    WAITING_RESPONSE: 'waiting_response',
    CONNECTING_WIFI: 'connecting_wifi',
    SUCCESS: 'success',
    FAILED: 'failed',
    CANCELLED: 'cancelled'
  };
  
  public static getInstance(): NetworkingFlowController {
    if (!NetworkingFlowController.instance) {
      NetworkingFlowController.instance = new NetworkingFlowController();
    }
    return NetworkingFlowController.instance;
  }
  
  private constructor() {
    this.wifiChecker = WifiStatusChecker.getInstance();
    this.passwordExecutor = PasswordDeliveryExecutor.getInstance();
  }
  
  /**
   * 启动完整配网流程
   */
  public async startNetworkingFlow(
    deviceId: string,
    onStateChange?: (state: NetworkingFlowState) => void
  ): Promise<NetworkingResult> {
    if (this.currentState !== NetworkingState.IDLE) {
      return {
        success: false,
        message: '已有配网流程在进行中',
        state: this.currentState
      };
    }
    
    this.currentState = NetworkingState.CHECKING_WIFI;
    this.currentDeviceId = deviceId;
    this.retryCount = 0;
    
    try {
      Logger.info('NetworkingFlowController', `启动配网流程,设备: ${deviceId}`);
      
      // 1. WiFi状态检查
      await this.executeWifiCheck(onStateChange);
      
      // 2. 网络选择(由UI层处理,这里等待用户输入)
      this.updateState(NetworkingState.SELECTING_NETWORK, onStateChange);
      
      // 流程在UI层继续,控制器提供执行方法
      return {
        success: true,
        message: '配网流程已启动',
        state: this.currentState,
        nextStep: 'select_network'
      };
      
    } catch (error) {
      Logger.error('NetworkingFlowController', `配网流程启动失败: ${error.message}`);
      
      this.currentState = NetworkingState.FAILED;
      this.updateState(this.currentState, onStateChange, {
        error: error.message
      });
      
      return {
        success: false,
        message: `启动失败: ${error.message}`,
        state: this.currentState
      };
    }
  }
  
  /**
   * 执行WiFi检查步骤
   */
  private async executeWifiCheck(onStateChange?: (state: NetworkingFlowState) => void): Promise<void> {
    this.updateState(NetworkingState.CHECKING_WIFI, onStateChange, {
      message: '正在检查WiFi状态...'
    });
    
    try {
      // 执行WiFi检查
      const wifiStatus = await this.wifiChecker.checkBasicStatus();
      
      // 检查结果处理
      if (!wifiStatus.isEnabled) {
        throw new Error('WiFi未开启,请先打开WiFi');
      }
      
      if (!wifiStatus.isConnected) {
        throw new Error('手机未连接WiFi,请先连接网络');
      }
      
      if (wifiStatus.level < 2) {
        throw new Error(`WiFi信号较弱 (${wifiStatus.level}/4),请靠近路由器`);
      }
      
      // WiFi状态良好,可以继续
      this.updateState(NetworkingState.CHECKING_WIFI, onStateChange, {
        message: `WiFi状态良好: ${wifiStatus.ssid} (${wifiStatus.level}/4)`,
        wifiStatus
      });
      
    } catch (error) {
      Logger.warn('NetworkingFlowController', `WiFi检查失败: ${error.message}`);
      throw error;
    }
  }
  
  /**
   * 执行密码下发步骤
   */
  public async executePasswordDelivery(
    ssid: string,
    password: string,
    onStateChange?: (state: NetworkingFlowState) => void
  ): Promise<NetworkingResult> {
    if (!this.currentDeviceId) {
      return {
        success: false,
        message: '未指定设备ID',
        state: this.currentState
      };
    }
    
    try {
      // 1. 连接设备
      this.updateState(NetworkingState.CONNECTING_DEVICE, onStateChange, {
        message: '连接门禁设备...'
      });
      
      // 2. 执行密码下发
      this.currentState = NetworkingState.DELIVERING_PASSWORD;
      
      const result = await this.passwordExecutor.executeDelivery(
        ssid,
        password,
        this.currentDeviceId,
        (progress) => {
          // 根据下发进度更新状态
          let state = this.currentState;
          let message = progress.message;
          
          if (progress.status === 'device_ack') {
            state = NetworkingState.WAITING_RESPONSE;
          } else if (progress.status === 'connecting_wifi') {
            state = NetworkingState.CONNECTING_WIFI;
          } else if (progress.status === 'success') {
            state = NetworkingState.SUCCESS;
          }
          
          this.updateState(state, onStateChange, {
            message,
            progress: progress.progress,
            details: progress.details
          });
        }
      );
      
      // 3. 处理结果
      if (result.success) {
        this.currentState = NetworkingState.SUCCESS;
        this.updateState(this.currentState, onStateChange, {
          message: '配网成功!',
          details: result.details
        });
      } else {
        this.currentState = NetworkingState.FAILED;
        this.updateState(this.currentState, onStateChange, {
          message: result.message,
          error: result.message
        });
      }
      
      return {
        success: result.success,
        message: result.message,
        state: this.currentState,
        details: result.details
      };
      
    } catch (error) {
      Logger.error('NetworkingFlowController', `密码下发失败: ${error.message}`);
      
      this.currentState = NetworkingState.FAILED;
      this.updateState(this.currentState, onStateChange, {
        message: `密码下发失败: ${error.message}`,
        error: error.message
      });
      
      return {
        success: false,
        message: error.message,
        state: this.currentState
      };
    }
  }
  
  /**
   * 重试当前步骤
   */
  public async retryCurrentStep(onStateChange?: (state: NetworkingFlowState) => void): Promise<NetworkingResult> {
    if (this.retryCount >= this.maxRetries) {
      this.currentState = NetworkingState.FAILED;
      this.updateState(this.currentState, onStateChange, {
        message: '已达到最大重试次数'
      });
      
      return {
        success: false,
        message: '已达到最大重试次数',
        state: this.currentState
      };
    }
    
    this.retryCount++;
    Logger.info('NetworkingFlowController', `${this.retryCount}次重试`);
    
    // 根据当前状态决定重试策略
    switch (this.currentState) {
      case NetworkingState.CHECKING_WIFI:
        return this.retryWifiCheck(onStateChange);
      case NetworkingState.CONNECTING_DEVICE:
      case NetworkingState.DELIVERING_PASSWORD:
      case NetworkingState.WAITING_RESPONSE:
        // 重置到设备连接步骤
        this.currentState = NetworkingState.CONNECTING_DEVICE;
        return {
          success: true,
          message: '已重置到设备连接步骤',
          state: this.currentState,
          nextStep: 'reconnect_device'
        };
      default:
        return {
          success: false,
          message: `当前状态不支持重试: ${this.currentState}`,
          state: this.currentState
        };
    }
  }
  
  /**
   * 重试WiFi检查
   */
  private async retryWifiCheck(onStateChange?: (state: NetworkingFlowState) => void): Promise<NetworkingResult> {
    try {
      await this.executeWifiCheck(onStateChange);
      return {
        success: true,
        message: 'WiFi检查重试成功',
        state: this.currentState
      };
    } catch (error) {
      return {
        success: false,
        message: `WiFi检查重试失败: ${error.message}`,
        state: this.currentState
      };
    }
  }
  
  /**
   * 取消配网流程
   */
  public cancelNetworkingFlow(): void {
    if (this.currentState !== NetworkingState.IDLE && 
        this.currentState !== NetworkingState.SUCCESS && 
        this.currentState !== NetworkingState.FAILED) {
      
      Logger.info('NetworkingFlowController', '取消配网流程');
      
      // 取消密码下发
      this.passwordExecutor.cancelDelivery();
      
      // 更新状态
      this.currentState = NetworkingState.CANCELLED;
      this.currentDeviceId = null;
      this.retryCount = 0;
    }
  }
  
  /**
   * 重置配网流程
   */
  public reset(): void {
    this.currentState = NetworkingState.IDLE;
    this.currentDeviceId = null;
    this.retryCount = 0;
    Logger.info('NetworkingFlowController', '配网流程已重置');
  }
  
  /**
   * 更新状态并通知
   */
  private updateState(
    state: NetworkingState,
    onStateChange?: (state: NetworkingFlowState) => void,
    data?: Record<string, any>
  ): void {
    this.currentState = state;
    
    const flowState: NetworkingFlowState = {
      state,
      deviceId: this.currentDeviceId,
      retryCount: this.retryCount,
      timestamp: Date.now(),
      ...data
    };
    
    // 回调通知
    if (onStateChange) {
      onStateChange(flowState);
    }
    
    // 全局事件通知
    emitter.emit({
      eventId: EventConstants.NETWORKING_FLOW_STATE
    }, { state: flowState });
    
    Logger.debug('NetworkingFlowController', `状态更新: ${state}`, data);
  }
  
  /**
   * 获取当前状态
   */
  public getCurrentState(): NetworkingFlowState {
    return {
      state: this.currentState,
      deviceId: this.currentDeviceId,
      retryCount: this.retryCount,
      timestamp: Date.now()
    };
  }
}

// 类型定义
type NetworkingState = 
  | 'idle'
  | 'checking_wifi'
  | 'selecting_network'
  | 'entering_password'
  | 'connecting_device'
  | 'delivering_password'
  | 'waiting_response'
  | 'connecting_wifi'
  | 'success'
  | 'failed'
  | 'cancelled';

export interface NetworkingFlowState {
  state: NetworkingState;
  deviceId?: string | null;
  retryCount: number;
  timestamp: number;
  message?: string;
  progress?: number;
  details?: Record<string, any>;
  error?: string;
}

export interface NetworkingResult {
  success: boolean;
  message: string;
  state: NetworkingState;
  nextStep?: string;
  details?: Record<string, any>;
}

5. 总结与最佳实践

5.1 实现要点总结

  1. 权限管理:严格遵守鸿蒙权限规范,动态申请必要的WiFi、蓝牙、位置权限
  2. 安全设计:密码加密传输、完整性校验、防重放攻击
  3. 错误处理:完善的异常捕获、重试机制、用户友好的错误提示
  4. 用户体验:清晰的进度反馈、状态可视化、操作引导
  5. 性能优化:异步操作、分块传输、资源及时释放

5.2 养老场景特别优化

  • 简化操作:一键式配网,减少护理员操作步骤
  • 容错设计:适应养老院复杂网络环境,自动重试和降级
  • 安全加固:密码本地加密,避免明文传输
  • 状态持久化:配网进度保存,支持断点续传

5.3 扩展建议

  1. 批量配网:支持同时配置多个门禁设备
  2. 网络切换:设备自动切换最优WiFi网络
  3. 远程协助:支持管理员远程协助配网
  4. 数据统计:配网成功率、时长等数据收集分析

通过本文的实现方案,益康养老HarmonyOS APP能够提供稳定、安全、易用的门禁设备WiFi配网功能,显著提升养老机构的物联网设备管理效率。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐