# 批量命令详解

## 概述

批量命令用于控制同一设备内的多个子硬件（如光柱1-480）。为了提供更好的用户体验和实时反馈，批量命令采用与 Complex 命令和程序上传相同的执行模式：通过 progress_update 报告进度，最后返回汇总响应。

## 核心理念

批量命令是针对同一设备内的多个子硬件（如光柱1-480）执行相同操作。设备端负责解析批量目标并执行，通过 progress_update 报告每个子硬件的执行进度。

## 重要限制

⚠️ **批量命令仅适用于同一类型的子硬件**：
- ✅ 可以：向编号1-20的光柱发送相同的颜色设置命令
- ✅ 可以：向编号1-5的显示屏发送相同的内容更新命令  
- ❌ 不可以：在同一批量命令中混合光柱和显示屏
- ❌ 不可以：在同一批量命令中混合不同型号的硬件

## 执行流程

```
Backend            Gateway              Edge              Device
   |                  |                   |                  |
   |  Batch Command   |                   |                  |
   |  (targets:"1-10")|                   |                  |
   |----------------->|                   |                  |
   |                  |                   |                  |
   |                  | Route to Edge     |                  |
   |                  |------------------>|                  |
   |                  |                   |                  |
   |                  |                   | Device内部处理    |
   |                  |                   |----------------->|
   |                  |                   |                  | 执行子硬件1
   |                  |                   |                  |
   |                  |                   | progress_update  |
   |                  |                   | (子硬件1)        |
   |                  |                   |<-----------------|
   |                  |                   |                  |
   |                  | progress_update   |                  |
   | progress_update  |<------------------|                  |
   | (子硬件1完成)    |                   |                  |
   |<-----------------|                   |                  |
   |                  |                   |                  | 执行子硬件2
   |                  |                   |                  |
   |                  |                   | progress_update  |
   |                  |                   | (子硬件2)        |
   |                  |                   |<-----------------|
   |                  |                   |                  |
   |                  | progress_update   |                  |
   | progress_update  |<------------------|                  |
   | (子硬件2完成)    |                   |                  |
   |<-----------------|                   |                  |
   |                  |                   |                  |
   |                  |                   |      ...         |
   |                  |                   |                  | 执行子硬件10
   |                  |                   |                  |
   |                  |                   | command_response |
   |                  |                   | (全部完成)       |
   |                  |                   |<-----------------|
   |                  |                   |                  |
   |                  | command_response  |                  |
   | command_response |<------------------|                  |
   | (summary)        |                   |                  |
   |<-----------------|                   |                  |
```

**注意**：批量命令是发送给同一个设备，由该设备内部处理多个子硬件的操作。

## 应用场景

### 典型硬件布局

```
隧道媒体广告设备端 (TC-01)
├─ 光柱系统 (1-480编号)
│  ├─ 光柱 001 ～ 020  (区域A)
│  ├─ 光柱 021 ～ 040  (区域B)
│  ├─ 光柱 041 ～ 060  (区域C)
│  │  ...
│  └─ 光柱 461 ～ 480  (区域Z)
├─ 同步器 (1编号)
├─ 测速器 (1-5编号)
└─ 通讯板 (1-10编号)
```

## 范围语法

批量命令支持灵活的范围语法来指定目标子硬件：

### 基本格式

- **单个目标**: `"5"` - 仅目标5
- **连续范围**: `"1-10"` - 目标1到10
- **离散目标**: `"1,3,5,7"` - 目标1、3、5、7
- **混合模式**: `"1-10,30-40,51,52,53,54"` - 组合使用
- **全部目标**: `"0"`  - 所有子硬件

### 语法示例

```javascript
// 范围解析示例
"1-5"          → [1, 2, 3, 4, 5]
"1,3,5"        → [1, 3, 5]
"1-3,7-9"      → [1, 2, 3, 7, 8, 9]
"1-100,200-300" → [1...100, 200...300]
"0"            → [所有可用设备]
```

## 消息结构

### 1. 批量命令请求（Backend → Gateway）

```json
{
  "type": "COMMAND",
  "requestRef": "batch-123",
  "targetClientId": "td-01",  // 设备ID
  "command": {
    "commandType": "BATCH",
    "commandCode": "SET_COLOR",
    "deviceType": "pillar",
    "deviceId": "1-10,30-40",  // 批量目标
    "operationType": "WRITE",
    "parameters": {
      "color": "#FF0000"
    }
  },
  "priority": "NORMAL",
  "timeout": 60000
}
```

### 2. Edge 转发批量命令到设备（Edge → Device）

```json
{
  "type": "COMMAND",
  "requestRef": "batch-123",
  "targetClientId": "td-01",  // 设备ID
  "command": {
    "commandType": "BATCH",
    "commandCode": "SET_COLOR",
    "deviceType": "pillar",
    "deviceId": "1-10,30-40",  // 子硬件范围
    "operationType": "WRITE",
    "parameters": {
      "color": "#FF0000",
      "targets": "1-10,30-40",  // 明确的目标列表
      "Ubatch": true
    }
  },
  "priority": "NORMAL",
  "timeout": 60000
}
```

### 3. 进度更新（Device → Edge → Gateway → Backend）

```json
{
  "type": "PROGRESS_UPDATE",
  "requestRef": "batch-123",
  "status": "IN_PROGRESS",
  "phase": "executing",
  "progress": 10,  // 1/10 子硬件完成
  "sourceType": "COMMAND",
  "command": {
    "commandType": "SIMPLE",
    "commandCode": "SET_COLOR",
    "deviceType": "pillar",
    "deviceId": 1,  // 子硬件ID
    "operationType": "WRITE",
    "result": {
      "previousColor": "#00FF00",
      "currentColor": "#FF0000"
    }
  },
  "log": {
    "level": "INFO",
    "message": "光柱 1 颜色设置成功"
  },
  "timestamp": "2024-01-20T10:00:01Z",
  "version": "1.0"
}
```

### 4. 最终响应（Device → Edge → Gateway → Backend）

```json
{
  "type": "COMMAND_RESPONSE",
  "requestRef": "batch-123",
  "status": "COMPLETED",
  "result": {
    "deviceType": "pillar",
    "deviceId": "1-10,30-40",
    "commandCode": "SET_COLOR",
    "operationType": "WRITE",
    "data": {
      "message": "批量设置颜色完成",
      "color": "#FF0000",
      "targetCount": 16,
      "successCount": 15,
      "failureCount": 1
    }
  },
  "summary": {
    "total": 16,
    "successful": 15,
    "Ufailed": 1,
    "failedTargets": [35]
  },
  "executionTime": 5200,
  "timestamp": "2024-01-20T10:00:05Z",
  "version": "1.0"
}
```

## 设备端实现指南

### 基本实现模式

```typescript
class DeviceHandler {
  async handleBatchCommand(message: CommandMessage) {
    const { command } = message;
    const targets = this.parseTargets(command.parameters.targets);
    let completedCount = 0;
    let successCount = 0;
    let failedTargets = [];
    
    // 处理每个子硬件
    for (const targetId of targets) {
      try {
        // 执行子硬件操作
        const result = await this.executeOnSubHardware(targetId, command);
        
        completedCount++;
        successCount++;
        
        // 发送进度更新
        await this.sendProgressUpdate({
          requestRef: message.requestRef,
          phase: 'executing',
          progress: (completedCount / targets.length) * 100,
          command: {
            commandType: 'simple',
            commandCode: command.commandCode,
            deviceType: command.deviceType,
            deviceId: targetId,  // 子硬件ID
            operationType: command.operationType,
            result
          },
          log: {
            level: 'info',
            message: `子硬件 ${targetId} 执行成功`
          }
        });
        
      } catch (error) {
        completedCount++;
        failedTargets.push(targetId);
        
        // 发送错误进度
        await this.sendProgressUpdate({
          requestRef: message.requestRef,
          phase: 'executing',
          progress: (completedCount / targets.length) * 100,
          command: {
            commandType: 'simple',
            commandCode: command.commandCode,
            deviceType: command.deviceType,
            deviceId: targetId,
            operationType: command.operationType,
            result: { error: error.message }
          },
          log: {
            level: 'error',
            message: `子硬件 ${targetId} 执行失败: ${error.message}`
          }
        });
      }
    }
    
    // 发送最终响应
    return {
      type: 'command_response',
      requestRef: message.requestRef,
      status: 'completed',
      result: {
        deviceType: command.deviceType,
        deviceId: command.deviceId,
        commandCode: command.commandCode,
        operationType: command.operationType,
        data: {
          message: '批量命令执行完成',
          targetCount: targets.length,
          successCount,
          failureCount: failedTargets.length
        }
      },
      summary: {
        total: targets.length,
        successful: successCount,
        failed: failedTargets.length,
        failedTargets
      }
    };
  }
  
  private parseTargets(targets: string | number[]): number[] {
    if (Array.isArray(targets)) {
      return targets;
    }
    
    // 解析 "1-10,30-40,51,52" 格式
    const result = [];
    const parts = targets.split(',');
    
    for (const part of parts) {
      if (part.includes('-')) {
        const [start, end] = part.split('-').map(Number);
        for (let i = start; i <= end; i++) {
          result.push(i);
        }
      } else {
        result.push(Number(part));
      }
    }
    
    return result;
  }
}
```

### 范围解析函数

```typescript
function parseRangeString(rangeStr: string): number[] {
  // 特殊情况：0 或 * 表示全部
  if (rangeStr === '0' || rangeStr === '*') {
    return getAllAvailableTargets();
  }
  
  const targets = new Set<number>();
  const parts = rangeStr.split(',').map(s => s.trim());
  
  for (const part of parts) {
    if (part.includes('-')) {
      // 处理范围：1-10
      const [start, end] = part.split('-').map(Number);
      if (!isNaN(start) && !isNaN(end) && start <= end) {
        for (let i = start; i <= end; i++) {
          targets.add(i);
        }
      }
    } else {
      // 处理单个数字
      const num = Number(part);
      if (!isNaN(num)) {
        targets.add(num);
      }
    }
  }
  
  return Array.from(targets).sort((a, b) => a - b);
}
```

## 最佳实践

### 1. 进度报告策略

- **合理的报告频率** - 避免过于频繁的进度更新
- **关键节点报告** - 在开始、25%、50%、75%、完成时报告

### 2. 错误处理

- **失败继续** - 单个子硬件失败不应停止整个批量操作
- **详细记录** - 记录每个失败的子硬件ID和原因
- **部分成功** - 支持部分成功的场景

### 3. 性能优化

```typescript
// 并行执行示例
async function executeBatchParallel(targets: number[], command: Command) {
  const batchSize = 10; // 每批处理10个
  const results = [];
  
  for (let i = 0; i < targets.length; i += batchSize) {
    const batch = targets.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(target => executeOnSubHardware(target, command))
    );
    results.push(...batchResults);
    
    // 报告批次进度
    await reportProgress(((i + batch.length) / targets.length) * 100);
  }
  
  return results;
}
```

### 4. 超时处理

```typescript
// 设置合理的超时时间
const timeoutMs = Math.max(
  5000,  // 最小5秒
  targets.length * 100  // 每个目标100ms
);
```

## 与其他命令类型的对比

| 特性 | Simple | Batch | Complex |
|------|--------|-------|---------|
| 目标数量 | 单个 | 多个子硬件 | 单个 |
| 响应次数 | 1次 | 多次(进度+结果) | 多次(进度+结果) |
| 进度反馈 | 无 | 有 | 有 |
| 适用场景 | 单一操作 | 批量控制 | 复杂任务 |
| 超时设置 | 较短 | 中等 | 较长 |

## 设计优势

1. **统一的执行模式** - 批量命令、Complex 命令、程序上传都使用 progress_update 报告进度
2. **更好的可观察性** - 实时看到每个子硬件的执行状态
3. **灵活的执行策略** - 设备可以控制子硬件的执行顺序
4. **清晰的职责划分** - Gateway 路由、Edge 管理连接、Device 执行操作

## 总结

批量命令通过引入 progress_update 机制，为批量操作提供了实时的执行反馈。这种设计既保证了协议的灵活性，又维持了实现的简洁性，为用户提供了良好的体验。