复杂协议控制

更新时间:2026-02-05 02:36:49下载pdf

为降低客户开发成本,方便扫地机与 App 面板交互,激光扫地机品类 SDK 把虚拟墙、房间属性等复杂控制,封装成不同的 API,应用开发者使用 API 即可完成数据通信。

功能介绍

当前 API 提供了以下方面的功能:

  • 完成云端下发的功能参数设置的协议解析,并将设置/查询命令及其参数通过回调接口交付业务应用。
  • 设备端功能参数上报 API 封装,设备端如有同云端的数据同步,调用相应的功能上报接口即可完成上报。

当前 API 支持的命令列表

功能 命令类型标识 发起方向
虚拟墙设置 VIRTUAL_WALL_SET 面板->设备
虚拟墙查询 VIRTUAL_WALL_QUERY 面板->设备
禁区设置 RESTRICTED_AREA_SET 面板->设备
禁区查询 RESTRICTED_AREA_QUERY 面板->设备
房间属性设置 ROOM_PROPERTY_SET 面板->设备
房间属性查询 ROOM_PROPERTY_QUERY 面板->设备
选区清扫设置 ROOM_CLEAN_SET 面板->设备
选区清扫查询 ROOM_CLEAN_QUERY 面板->设备
划区清扫设置 ZONE_CLEAN_SET 面板->设备
划区清扫查询 ZONE_CLEAN_QUERY 面板->设备
定点清扫设置 SPOT_CLEAN_SET 面板->设备
定点清扫查询 SPOT_CLEAN_QUERY 面板->设备
预约定时设置 SCHEDULE_SET 面板->设备
预约定时查询 SCHEDULE_QUERY 面板->设备
勿扰定时设置 QUIET_HOUR_SET 面板->设备
勿扰定时查询 QUIET_HOUR_QUERY 面板->设备
地图分区分割 PART_DIVI_SET 面板->设备
地图分区合并 PART_MERGE_SET 面板->设备
地图分区恢复默认 PART_DEFAULT_SET 面板->设备
地图重置 RESET_CURR_MAP_SET 面板->设备
地图保存 SAVE_CURR_MAP_SET 面板->设备
云端地图删除 DELETE_CLOUD_MAP_SET 面板->设备
语音查询 VOICE_LANGUAGE_QUERY 面板->设备
设备信息查询 DEV_INFO_QUERY 面板->设备
密码状态查询 PASSWORD_STATE_QUERY 面板->设备
密码验证 PASSWORD_CHECK 面板->设备
密码设置 PASSWORD_SET 面板->设备
查询当前房间信息 MCS_ROOM_INFO_QUERY 面板->设备

触发时机说明

SDK 通过注册的回调函数向应用传递命令,命令触发分为以下三种场景:

1. 用户主动操作触发(SET 命令)

触发场景:用户在 App 面板上进行操作时触发

用户操作 触发命令 触发时机
进入地图编辑界面,在地图上绘制虚拟墙 VIRTUAL_WALL_SET 用户点击并确认后
进入地图编辑界面,设置禁区区域 RESTRICTED_AREA_SET 用户完成禁区绘制并确认后
进入地图编辑界面,修改房间名称/清扫参数 ROOM_PROPERTY_SET 用户修改房间属性后立即触发
进入首页地图界面,选择房间清扫 ROOM_CLEAN_SET 用户选择房间并点击开始清扫
进入首页地图界面,划区清扫 ZONE_CLEAN_SET 用户在地图上划定区域并点击开始清扫
进入首页地图界面,定点清扫 SPOT_CLEAN_SET 用户点击地图上的某个点位并点击开始清扫
进入设备设置界面,创建/修改预约清扫 SCHEDULE_SET 用户设置定时任务后
进入设备设置界面,设置勿扰时段 QUIET_HOUR_SET 用户配置勿扰时间后
进入地图编辑界面,分割房间 PART_DIVI_SET 用户在地图上划分割线
进入地图编辑界面,合并房间 PART_MERGE_SET 用户选择两个房间合并
进入地图编辑界面,恢复默认分区 PART_DEFAULT_SET 用户点恢复默认按钮
进入地图编辑界面,重置地图 RESET_CURR_MAP_SET 用户点击"重置地图"确认后
进入地图编辑界面,保存地图 SAVE_CURR_MAP_SET 用户点击保存当前地图
进入多地图管理界面,删除云端地图 DELETE_CLOUD_MAP_SET 用户选择地图并点击删除
进入视频通道,验证密码 PASSWORD_CHECK 用户输入密码提交验证
进入视频通道,设置密码 PASSWORD_SET 用户设置新密码

处理流程

用户操作 → App 面板 → 云端 → SDK 解析 → FUNC_SET_CB 回调 → 应用处理 → 调用response 接口上报结果

2. 面板刷新查询(QUERY 命令)

触发场景:用户打开 App 面板或下拉刷新时触发

查询命令 触发时机 预期响应
VIRTUAL_WALL_QUERY 进入首页地图页面 返回当前地图上所有虚拟墙
RESTRICTED_AREA_QUERY 进入首页地图页面 返回当前地图上所有禁区
ROOM_PROPERTY_QUERY 进入首页地图页面 返回所有房间的属性信息
ROOM_CLEAN_QUERY 选区清扫页面 返回上次选区清扫的配置
ZONE_CLEAN_QUERY 划区清扫页面 返回上次划区清扫的参数
SPOT_CLEAN_QUERY 定点清扫页面 返回上次定点清扫的配置
SCHEDULE_QUERY 打开定时任务页面 返回所有预约清扫任务
QUIET_HOUR_QUERY 打开勿扰设置页面 返回勿扰时段配置
VOICE_LANGUAGE_QUERY 进入语音设置页面 返回当前语音包状态
DEV_INFO_QUERY 进入设备设置页面 返回设备详细信息
MAP_EMPTY_QUERY App 首页地图加载(备用功能) 返回是否存在地图数据
PASSWORD_STATE_QUERY 进入密码设置页面 返回密码是否已设置
MCS_ROOM_INFO_QUERY Alexa 语音控制 返回设备当前所在房间

处理流程

面板刷新 → 云端 → SDK 解析 → FUNC_QUERY_CB 回调 → 应用查询数据 → 调用response 接口上报结果

功能解析与结构体使用

虚拟墙设置 & 查询

功能说明:虚拟墙是在地图上设置的虚拟屏障线,机器人不会跨越该线进行清扫。

数据结构

```C
/**
* @brief Forbidden modes
*/
typedef enum {
    FORBIT_ALL = 0, // All prohibited
    FORBIT_SWEEP, // Prohibit mopping
    FORBIT_MOP, // Prohibit sweeping
    FORBIT_RESERVE, // Not set, reserved
} FORBIT_MODE_E;

/**
* @brief Coordinate point structure
*/
typedef struct {
    int x; // X value of the coordinate point
    int y; // Y value of the coordinate point
} POINT_COOR_S;

/**
* @brief Virtual line coordinate structure
*/
typedef struct {
    FORBIT_MODE_E mode; // Forbidden mode
    POINT_COOR_S points[2]; // Endpoints of the line, first element is the starting point of the virtual wall, second element is the ending point of the virtual wall
} VIRTUAL_LINE_S;

/**
* @brief Virtual wall structure
*/
typedef struct {
    int num; // Number of virtual walls
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    VIRTUAL_LINE_S* line; // Virtual wall coordinate points and forbidden mode
} VIRTUAL_WALL_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收面板设置的虚拟墙数量 填充当前虚拟墙总数 填充当前虚拟墙总数
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
line 数组指针 解析每条虚拟墙的坐标和模式 填充所有虚拟墙信息 填充所有虚拟墙信息
line[i].mode 枚举 虚拟墙模式(全禁/禁扫/禁拖) 填充虚拟墙模式 填充虚拟墙模式
line[i].points[0] 坐标 虚拟墙起点 (x0, y0) 填充起点坐标 填充起点坐标
line[i].points[1] 坐标 虚拟墙终点 (x1, y1) 填充终点坐标 填充终点坐标

使用示例

// SET命令处理示例
static OPERATE_RET handle_virtual_wall_set(VIRTUAL_WALL_S* p_virtual_wall)
{
    PR_DEBUG("收到虚拟墙设置命令,虚拟墙数量: %d", p_virtual_wall->num);
    
    // 遍历所有虚拟墙
    for(int i = 0; i < p_virtual_wall->num; i++) {
        PR_DEBUG("虚拟墙[%d] 模式: %d", i, p_virtual_wall->line[i].mode);
        PR_DEBUG("  起点: (%d, %d)", 
                 p_virtual_wall->line[i].points[0].x,
                 p_virtual_wall->line[i].points[0].y);
        PR_DEBUG("  终点: (%d, %d)", 
                 p_virtual_wall->line[i].points[1].x,
                 p_virtual_wall->line[i].points[1].y);
        
        // TODO: 将虚拟墙数据保存到设备内存/持久化存储
        // save_virtual_wall_to_device(i, &p_virtual_wall->line[i]); //伪代码,需要开发者自行实现
    }
    
    // 处理完成后上报结果
    VIRTUAL_WALL_S report_data;
    report_data.num = p_virtual_wall->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.line = p_virtual_wall->line;
    
    return ty_rvc_virtual_wall_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_virtual_wall_query(void)
{
    PR_DEBUG("收到虚拟墙查询命令");
    
    // 从设备获取当前虚拟墙数据
    VIRTUAL_WALL_S query_data;
    VIRTUAL_LINE_S lines[10];  // 假设最多10条虚拟墙
    
    query_data.num = get_virtual_wall_count();  // 获取虚拟墙数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();   // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.line = lines;
    
    // 填充每条虚拟墙数据
    for(int i = 0; i < query_data.num; i++) {
        // load_virtual_wall_from_device(i, &lines[i]);//伪代码,需要开发者自行实现
        lines[i].mode = FORBIT_ALL;
        lines[i].points[0].x = 100;
        lines[i].points[0].y = 200;
        lines[i].points[1].x = 500;
        lines[i].points[1].y = 200;
    }
    
    return ty_rvc_virtual_wall_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 坐标系说明
    • 虚拟墙坐标使用机器坐标系(以机器人起始位置为原点 O)
    • 坐标类型:INT32 整数值
    • 坐标含义:坐标点 M(x, y) 表示该点相对于原点 O 的栅格数量
    • 实际位置计算:实际距离 = 栅格数量 × 栅格分辨率
    • 示例:若栅格分辨率为 5cm,坐标 (100, 200) 表示距离原点 (500cm, 1000cm) = (5m, 10m)
    • 具体可参考地图坐标系说明
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据拷贝到应用自己分配的内存中
    • 设备上报(response接口)line 数组及相关内存由应用自行分配和管理,SDK不会释放该内存

禁区设置 & 查询

功能说明:禁区是地图上的多边形区域(通常为矩形),机器人不会进入该区域清扫。

数据结构

```c
typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

/**
* @brief Area coordinate point structure
*/
typedef struct {
    int point_num; // Number of endpoints forming the area
    POINT_COOR_S* point; // Endpoint coordinates forming the restricted area
} AREA_S;

/**
* @brief Virtual area structure
*/
typedef struct {
    FORBIT_MODE_E mode; // Forbidden mode
    AREA_S area; // Endpoint coordinates of the restricted area
    STR_ELEMENT_S cur_name; // Name of the restricted area
} VIRTUAL_AREA_S;

/**
* @brief Restricted area structure
*/
typedef struct {
    int num; // Number of restricted areas
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    VIRTUAL_AREA_S* restrict_zone; // Coordinates and forbidden mode of the restricted area
} RESTRICTED_AREA_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收面板设置的禁区数量 填充当前禁区总数 填充当前禁区总数
map_id int 面板不下发,忽略该字段 必须填充当前地图 ID 必须填充当前地图ID
restrict_zone 数组指针 解析每个禁区的信息 填充所有禁区信息 填充所有禁区信息
restrict_zone[i].mode 枚举 禁区模式(全禁/禁扫/禁拖) 填充禁区模式 填充禁区模式
restrict_zone[i].area 区域结构 禁区的多边形坐标点集合 填充区域坐标 填充区域坐标
restrict_zone[i].area.point_num int 禁区顶点数量(通常为4) 填充顶点数量 填充顶点数量
restrict_zone[i].area.point 坐标数组 禁区各顶点坐标 填充各顶点坐标 填充各顶点坐标
restrict_zone[i].cur_name 字符串 禁区名称 填充禁区名称 填充禁区名称

使用示例

// SET命令处理示例
static OPERATE_RET handle_restricted_area_set(RESTRICTED_AREA_S* p_restricted_area)
{
    PR_DEBUG("收到禁区设置命令,禁区数量: %d", p_restricted_area->num);
    
    // 遍历所有禁区
    for(int i = 0; i < p_restricted_area->num; i++) {
        PR_DEBUG("禁区[%d] 模式: %d", i, p_restricted_area->restrict_zone[i].mode);
        PR_DEBUG("  名称: %.*s", 
                 p_restricted_area->restrict_zone[i].cur_name.len,
                 p_restricted_area->restrict_zone[i].cur_name.name);
        PR_DEBUG("  顶点数量: %d", 
                 p_restricted_area->restrict_zone[i].area.point_num);
        
        // 遍历禁区的所有顶点
        for(int j = 0; j < p_restricted_area->restrict_zone[i].area.point_num; j++) {
            PR_DEBUG("    顶点[%d]: (%d, %d)", j,
                     p_restricted_area->restrict_zone[i].area.point[j].x,
                     p_restricted_area->restrict_zone[i].area.point[j].y);
        }
        
        // TODO: 将禁区数据保存到设备内存/持久化存储
        // save_restricted_area_to_device(i, &p_restricted_area->restrict_zone[i]);//伪代码,需要开发者自行实现
    }
    
    // 处理完成后上报结果
    RESTRICTED_AREA_S report_data;
    report_data.num = p_restricted_area->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.restrict_zone = p_restricted_area->restrict_zone;
    
    return ty_rvc_restricted_area_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_restricted_area_query(void)
{
    PR_DEBUG("收到禁区查询命令");
    
    // 从设备获取当前禁区数据
    RESTRICTED_AREA_S query_data;
    VIRTUAL_AREA_S areas[5];  // 假设最多5个禁区
    POINT_COOR_S area_points[5][4];  // 每个禁区4个顶点
    
    query_data.num = get_restricted_area_count();  // 获取禁区数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();      // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.restrict_zone = areas;
    
    // 填充每个禁区数据
    for(int i = 0; i < query_data.num; i++) {
        // load_restricted_area_from_device(i, &areas[i]);//伪代码,需要开发者自行实现
        
        // 示例:设置禁区模式
        areas[i].mode = FORBIT_ALL;
        
        // 示例:设置禁区名称
        areas[i].cur_name.name = "禁区1";
        areas[i].cur_name.len = strlen(areas[i].cur_name.name);
        
        // 示例:设置禁区区域(矩形4个顶点)
        areas[i].area.point_num = 4;
        areas[i].area.point = area_points[i];
        
        // 填充矩形4个顶点坐标(逆时针或顺时针)
        area_points[i][0].x = 100; area_points[i][0].y = 100;  // 左下
        area_points[i][1].x = 200; area_points[i][1].y = 100;  // 右下
        area_points[i][2].x = 200; area_points[i][2].y = 200;  // 右上
        area_points[i][3].x = 100; area_points[i][3].y = 200;  // 左上
    }
    
    return ty_rvc_restricted_area_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 禁区形状
    • 通常为矩形区域(4个顶点)
    • 顶点坐标需按顺序排列(顺时针或逆时针)
    • 支持多边形禁区,根据 point_num 确定顶点数量
  • 坐标系说明
    • 禁区坐标使用机器坐标系(与虚拟墙相同)
    • 坐标类型:INT32 整数值,表示栅格数量
    • 实际位置计算:实际距离 = 栅格数量 × 栅格分辨率
    • 具体可参考地图坐标系说明
  • 字符串处理
    • cur_name 字段需要同时填充 name 指针和 len 长度
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如放入队列),必须先将数据拷贝到应用自己分配的内存中
    • 设备上报(response接口)restrict_zone 数组、内部的 point 数组以及 cur_name.name 字符串内存由应用自行分配和管理,SDK不会释放这些内存

房间属性设置 & 查询

功能说明:房间属性管理包括房间命名、清扫模式、清扫顺序、地板类型等参数配置。

数据结构

```C
/**
* @brief Cleaning parameters
*/
typedef struct {
    int valid; // Whether the parameter is valid
    STR_ELEMENT_S suction; // Suction power setting, specific values should be agreed upon with the project
    STR_ELEMENT_S cistern; // Water volume setting, specific values should be agreed upon with the project
    bool y_mop; // Y-shaped mopping, TRUE for Y-shaped mopping to take effect
    int clean_cnt; // Number of room cleanings
} CLEAN_PARAM_S;

/**
* @brief Room property structure
*/
typedef struct {
    int num; // Number of rooms
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    int* ids; // Room ID numbers, each room corresponds to one ID number
    STR_ELEMENT_S* sweep_mode; // room sweep mode,each room corresponds to one sweep mode
    CLEAN_PARAM_S* param; // Cleaning parameters for rooms, each room corresponds to one cleaning setting parameter, param[0] corresponds to ids[0] area
    int* orders; // Cleaning order for rooms, each room corresponds to one cleaning order, cleaning order is represented by non-zero Arabic numerals, smaller values indicate higher cleaning priority, zero represents the lowest priority
    STR_ELEMENT_S* cus_name; // Room naming list, each room corresponds to a name, cus_name[0] corresponds to id[0]
    int* floor_type; // Room floor types,each room corresponds to one floor type
} ROOM_PROPERTY_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收房间数量 填充当前房间总数 填充当前房间总数
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
ids int数组 解析各房间ID 填充所有房间ID 填充所有房间ID
sweep_mode 字符串数组 解析清扫模式(字符串格式) 填充清扫模式字符串 填充清扫模式字符串
param 清扫参数数组 解析各房间清扫参数 填充各房间清扫参数 填充各房间清扫参数
param[i].valid int 参数有效标志(备用字段) 填充有效标志 填充有效标志
param[i].suction 字符串 吸力档位(字符串格式) 填充吸力字符串 填充吸力字符串
param[i].cistern 字符串 水量档位(字符串格式) 填充水量字符串 填充水量字符串
param[i].y_mop bool Y字拖地开关 填充Y字拖开关 填充Y字拖开关
param[i].clean_cnt int 清扫次数 填充清扫次数 填充清扫次数
orders int数组 清扫顺序(数字越小优先级越高) 填充清扫顺序 填充清扫顺序
cus_name 字符串数组 房间自定义名称 填充房间名称 填充房间名称
floor_type int数组 地板类型(备用字段,如需实现,需要与面板对齐地板类型) 填充地板类型 填充地板类型

使用示例

// SET命令处理示例
static OPERATE_RET handle_room_property_set(ROOM_PROPERTY_S* p_room_property)
{
    PR_DEBUG("收到房间属性设置命令,房间数量: %d", p_room_property->num);
    
    // 遍历所有房间
    for(int i = 0; i < p_room_property->num; i++) {
        PR_DEBUG("房间[%d] ID: %d", i, p_room_property->ids[i]);
        
        // 解析房间名称
        if(p_room_property->cus_name) {
            PR_DEBUG("  名称: %d-%s", 
                     p_room_property->cus_name[i].len,
                     p_room_property->cus_name[i].name);
        }
        
        // 解析清扫模式(字符串格式,需转换为枚举)
        if(p_room_property->sweep_mode) {
            PR_DEBUG("  清扫模式: %d-%s", 
                     p_room_property->sweep_mode[i].len,
                     p_room_property->sweep_mode[i].name);
            // TODO: 将字符串转换为枚举值
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_room_property->sweep_mode[i]); //伪代码,需要开发者自行实现
        }
        
        // 解析清扫参数
        if(p_room_property->param) {
            PR_DEBUG("  吸力: %d-%s", 
                     p_room_property->param[i].suction.len,
                     p_room_property->param[i].suction.name);
            PR_DEBUG("  水量: %d-%s", 
                     p_room_property->param[i].cistern.len,
                     p_room_property->param[i].cistern.name);
            PR_DEBUG("  Y字拖: %d", p_room_property->param[i].y_mop);
            PR_DEBUG("  清扫次数: %d", p_room_property->param[i].clean_cnt);
        }
        
        // 解析清扫顺序
        if(p_room_property->orders) {
            PR_DEBUG("  清扫顺序: %d", p_room_property->orders[i]);
        }
        
        // 解析地板类型
        if(p_room_property->floor_type) {      //预留功能,如需实现,需要与面板对齐地板类型
            PR_DEBUG("  地板类型: %d", p_room_property->floor_type[i]);
        }
        
        // TODO: 将房间属性保存到设备内存/持久化存储
        // save_room_property_to_device(i, ...); //伪代码,需要开发者自行实现
    }
    
    // 处理完成后上报结果
    ROOM_PROPERTY_S report_data;
    report_data.num = p_room_property->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.ids = p_room_property->ids;
    report_data.sweep_mode = p_room_property->sweep_mode;
    report_data.param = p_room_property->param;
    report_data.orders = p_room_property->orders;
    report_data.cus_name = p_room_property->cus_name;
    report_data.floor_type = p_room_property->floor_type;
    
    return ty_rvc_room_property_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_room_property_query(void)
{
    PR_DEBUG("收到房间属性查询命令");
    
    // 从设备获取当前房间属性数据
    ROOM_PROPERTY_S query_data;
    int room_ids[10];           // 房间ID数组
    STR_ELEMENT_S sweep_modes[10];  // 清扫模式数组
    CLEAN_PARAM_S params[10];   // 清扫参数数组
    int orders[10];             // 清扫顺序数组
    STR_ELEMENT_S room_names[10];   // 房间名称数组
    int floor_types[10];        // 地板类型数组
    
    query_data.num = get_room_count();     // 获取房间数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();  // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.ids = room_ids;
    query_data.sweep_mode = sweep_modes;
    query_data.param = params;
    query_data.orders = orders;
    query_data.cus_name = room_names;
    query_data.floor_type = floor_types;
    
    // 填充每个房间的数据
    for(int i = 0; i < query_data.num; i++) {
        // 填充房间ID
        room_ids[i] = get_room_id(i);
        
        // 填充房间名称
        room_names[i].name = get_room_name(i);  // 例如: "客厅"
        room_names[i].len = strlen(room_names[i].name);
        
        // 填充清扫模式(字符串格式)
        sweep_modes[i].name = "only_sweep";  // 例如: "only_sweep","only_mop","both_work"
        sweep_modes[i].len = strlen(sweep_modes[i].name);
        
        // 填充清扫参数
        params[i].suction.name = "strong";  // 例如: closed","gentle","normal","strong","max
        params[i].suction.len = strlen(params[i].suction.name);
        params[i].cistern.name = "middle";  // 例如: closed","low","middle","high"
        params[i].cistern.len = strlen(params[i].cistern.name);
        params[i].y_mop = true;
        params[i].clean_cnt = 2;
        
        // 填充清扫顺序(1最高,0最低)
        orders[i] = i + 1;
        
        // 填充地板类型(预留字段)
        floor_types[i] = 1;  // 例如: 0=瓷砖, 1=木地板, 2=地毯
    }
    
    return ty_rvc_room_property_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 重要:任何涉及房间编辑的操作(地图分区分割、合并、恢复默认、切换地图、创建新地图等),完成后都必须主动上报房间属性,确保 App 面板显示最新的房间信息
  • 房间属性变更包括:房间数量变化、房间ID变化、房间名称变化、房间参数变化等
  • 建议在房间编辑操作的响应函数中,调用 ty_rvc_room_property_data_response() 上报最新房间属性
  • 字符串字段处理
    • sweep_modecus_namesuctioncistern 均为字符串格式
    • 每个字符串需要同时填充 name 指针和 len 长度
    • 字符串内存由应用管理,注意内存生命周期
  • 字符串转枚举
    • 清扫模式示例:"only_sweep" / "only_mop" / "both_work"
    • 吸力档位示例:"closed" / "gentle" / ""normal"" / "strong" / "max"
    • 水量档位示例:"closed" / "low" / "middle" / "high"
  • 清扫顺序规则
    • 数字越小优先级越高(1 > 2 > 3 > …)
    • 0 表示不参与排序
  • 数组索引对应关系
    • 所有数组索引与 ids 数组一一对应
    • ids[0] 对应 cus_name[0]sweep_mode[0]param[0]orders[0]floor_type[0]
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据(包括所有数组和字符串)拷贝到应用自己分配的内存中
    • 设备上报(response接口):所有数组(idssweep_modeparamordersfloor_materialroom_name 等)及字符串内存(suction.namecistern.namesweep_mode[i].name 等)由应用自行分配和管理,SDK不会释放这些内存

选区清扫设置 & 查询

功能说明:选区清扫允许用户选择一个或多个房间进行清扫,可为每个房间配置独立的清扫模式和参数。

数据结构

```c
/**
* @brief Cleaning parameters
*/
typedef struct {
    int valid; // Whether the parameter is valid
    STR_ELEMENT_S suction; // Suction power setting, specific values should be agreed upon with the project
    STR_ELEMENT_S cistern; // Water volume setting, specific values should be agreed upon with the project
    bool y_mop; // Y-shaped mopping, TRUE for Y-shaped mopping to take effect
    int clean_cnt; // Number of room cleanings
} CLEAN_PARAM_S;

/**
* @brief Selected area cleaning structure
*/
typedef struct {
    int num; // Number of selected areas
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    int* ids; // List of IDs for the selected area rooms
    STR_ELEMENT_S* sweep_mode; // sweep mode for the selected area rooms
    CLEAN_PARAM_S* param; // Cleaning parameters for selected areas, each room corresponds to one cleaning parameter, param[0] corresponds to ids[0] area, if not set valid is 0
} ROOM_CLEAN_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收选中的房间数量 填充上次选区清扫的房间数量 填充当前选区清扫的房间数量
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
ids int数组 解析选中的房间ID列表 填充上次选中的房间ID 填充当前选中的房间ID
sweep_mode 字符串数组 解析各房间清扫模式(字符串格式) 填充各房间清扫模式字符串 填充各房间清扫模式字符串
param 清扫参数数组 解析各房间清扫参数 填充各房间清扫参数 填充各房间清扫参数
param[i].valid int 参数有效标志(备用字段) 填充有效标志 填充有效标志
param[i].suction 字符串 吸力档位(字符串格式) 填充吸力字符串 填充吸力字符串
param[i].cistern 字符串 水量档位(字符串格式) 填充水量字符串 填充水量字符串
param[i].y_mop bool Y字拖地开关 填充Y字拖开关 填充Y字拖开关
param[i].clean_cnt int 清扫次数 填充清扫次数 填充清扫次数

使用示例

// SET命令处理示例
static OPERATE_RET handle_room_clean_set(ROOM_CLEAN_S* p_room_clean)
{
    PR_DEBUG("收到选区清扫设置命令,房间数量: %d", p_room_clean->num);
    
    // 遍历所有选中的房间
    for(int i = 0; i < p_room_clean->num; i++) {
        PR_DEBUG("房间[%d] ID: %d", i, p_room_clean->ids[i]);
        
        // 解析清扫模式(字符串格式,需转换为枚举)
        if(p_room_clean->sweep_mode) {
            PR_DEBUG("  清扫模式:%d-%s", 
                     p_room_clean->sweep_mode[i].len,
                     p_room_clean->sweep_mode[i].name);
            // TODO: 将字符串转换为枚举值
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_room_clean->sweep_mode[i]);//伪代码,需要开发者自行实现
        }
        
        // 解析清扫参数
        PR_DEBUG("  吸力: %d-%s", 
                    p_room_clean->param[i].suction.len,
                    p_room_clean->param[i].suction.name);
        PR_DEBUG("  水量: %d-%s", 
                    p_room_clean->param[i].cistern.len,
                    p_room_clean->param[i].cistern.name);
        PR_DEBUG("  Y字拖: %d", p_room_clean->param[i].y_mop);
        PR_DEBUG("  清扫次数: %d", p_room_clean->param[i].clean_cnt);
        
        // TODO: 保存选区清扫配置
        // save_room_clean_config(i, p_room_clean->ids[i], ...);//伪代码,需要开发者自行实现
    }
    
    // 启动选区清扫任务
    // start_room_clean_task(p_room_clean->ids, p_room_clean->num);//伪代码,需要开发者自行实现
    
    // 处理完成后上报结果
    ROOM_CLEAN_S report_data;
    report_data.num = p_room_clean->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.ids = p_room_clean->ids;
    report_data.sweep_mode = p_room_clean->sweep_mode;
    report_data.param = p_room_clean->param;
    
    return ty_rvc_room_clean_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_room_clean_query(void)
{
    PR_DEBUG("收到选区清扫查询命令");
    
    // 从设备获取上次选区清扫配置
    ROOM_CLEAN_S query_data;
    int room_ids[10];               // 房间ID数组
    STR_ELEMENT_S sweep_modes[10];  // 清扫模式数组
    CLEAN_PARAM_S params[10];       // 清扫参数数组
    
    // 获取上次选区清扫的配置
    query_data.num = get_last_room_clean_count();  // 获取上次选中的房间数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();      // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.ids = room_ids;
    query_data.sweep_mode = sweep_modes;
    query_data.param = params;
    
    // 填充每个房间的数据
    for(int i = 0; i < query_data.num; i++) {
        // 填充房间ID
        room_ids[i] = get_last_clean_room_id(i);
        
        // 填充清扫模式(字符串格式)
        sweep_modes[i].name = "both_work";  // 例如: "only_sweep","only_mop","both_work"
        sweep_modes[i].len = strlen(sweep_modes[i].name);
        
        // 填充清扫参数
        params[i].suction.name = "strong";  // 例如: "closed","gentle","normal","strong","max"
        params[i].suction.len = strlen(params[i].suction.name);
        params[i].cistern.name = "high";    // 例如: "closed","low","middle","high"
        params[i].cistern.len = strlen(params[i].cistern.name);
        params[i].y_mop = false;
        params[i].clean_cnt = 1;
    }
    
    return ty_rvc_room_clean_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 字符串字段处理
    • sweep_modesuctioncistern 均为字符串格式
    • 每个字符串需要同时填充 name 指针和 len 长度
    • 字符串内存由应用管理,注意内存生命周期
  • 字符串转枚举
    • 清扫模式示例:"only_sweep" / "only_mop" / "both_work"
    • 吸力档位示例:"closed" / "gentle" / "normal" / "strong" / "max"
    • 水量档位示例:"closed" / "low" / "middle" / "high"
  • 数组索引对应关系
    • 所有数组索引与 ids 数组一一对应
    • ids[0] 对应 sweep_mode[0]param[0]
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据(包括所有数组和字符串)拷贝到应用自己分配的内存中
    • 设备上报(response接口):所有数组(idsparam)及字符串内存(suction.namecistern.namesweep_mode[i].name等)由应用自行分配和管理,SDK不会释放这些内存

划区清扫设置 & 查询

功能说明:划区清扫允许用户在地图上框选任意多边形区域进行清扫,每个区域可独立配置清扫参数。

数据结构

```c
/**
* @brief Area coordinate point structure
*/
typedef struct {
    int point_num; // Number of endpoints forming the area
    POINT_COOR_S* point; // Endpoint coordinates forming the restricted area
} AREA_S;

/**
* @brief Zone parameters structure
*/
typedef struct {
    STR_ELEMENT_S mode; // Cleaning mode
    CLEAN_PARAM_S param; // Cleaning parameters
    AREA_S area; // Area coordinates
    STR_ELEMENT_S cur_name; // Area name
} ZONE_AREA_PARAM_S;
/**
* @brief Zone cleaning structure
*/
typedef struct {
    int num; // Number of zones
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    ZONE_AREA_PARAM_S* clean_zone; // Cleaning parameters for zone cleaning, each zone corresponds to one cleaning parameter
} ZONE_CLEAN_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收划区数量 填充上次划区清扫的区域数量 填充当前划区清扫的区域数量
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
clean_zone 区域参数数组 解析所有划区的参数 填充上次划区参数 填充当前划区参数
clean_zone[i].mode 字符串 清扫模式(字符串格式) 填充清扫模式字符串 填充清扫模式字符串
clean_zone[i].param 清扫参数 该区域的清扫参数 填充清扫参数 填充清扫参数
clean_zone[i].param.valid int 参数有效标志(备用字段) 填充有效标志 填充有效标志
clean_zone[i].param.suction 字符串 吸力档位(字符串格式) 填充吸力字符串 填充吸力字符串
clean_zone[i].param.cistern 字符串 水量档位(字符串格式) 填充水量字符串 填充水量字符串
clean_zone[i].param.y_mop bool Y字拖地开关 填充Y字拖开关 填充Y字拖开关
clean_zone[i].param.clean_cnt int 清扫次数 填充清扫次数 填充清扫次数
clean_zone[i].area 区域结构 划区的多边形坐标集合 填充区域坐标 填充区域坐标
clean_zone[i].area.point_num int 多边形顶点数量(通常4个) 填充顶点数量 填充顶点数量
clean_zone[i].area.point 坐标数组 多边形各顶点坐标 填充各顶点坐标 填充各顶点坐标
clean_zone[i].cur_name 字符串 划区名称 填充划区名称 填充划区名称

使用示例

// SET命令处理示例
static OPERATE_RET handle_zone_clean_set(ZONE_CLEAN_S* p_zone_clean)
{
    PR_DEBUG("收到划区清扫设置命令,划区数量: %d", p_zone_clean->num);
    
    // 遍历所有划区
    for(int i = 0; i < p_zone_clean->num; i++) {
        PR_DEBUG("划区[%d]:", i);
        
        // 解析划区名称
        if(p_zone_clean->clean_zone[i].cur_name.name) {
            PR_DEBUG("  名称: %d-%s", 
                     p_zone_clean->clean_zone[i].cur_name.len,
                     p_zone_clean->clean_zone[i].cur_name.name);
        }
        
        // 解析清扫模式(字符串格式,需转换为枚举)
        PR_DEBUG("  清扫模式: %d-%s", 
                 p_zone_clean->clean_zone[i].mode.len,
                 p_zone_clean->clean_zone[i].mode.name);
        // TODO: 将字符串转换为枚举值
        // int mode_enum = convert_sweep_mode_str_to_enum(&p_zone_clean->clean_zone[i].mode);//伪代码,需要开发者自行实现
        
        // 解析清扫参数
        CLEAN_PARAM_S* param = &p_zone_clean->clean_zone[i].param;

        PR_DEBUG("  吸力: %d-%s", param->suction.len, param->suction.name);
        PR_DEBUG("  水量: %d-%s", param->cistern.len, param->cistern.name);
        PR_DEBUG("  Y字拖: %d", param->y_mop);
        PR_DEBUG("  清扫次数: %d", param->clean_cnt);

        
        // 解析划区区域坐标(多边形顶点)
        AREA_S* area = &p_zone_clean->clean_zone[i].area;
        PR_DEBUG("  顶点数量: %d", area->point_num);
        for(int j = 0; j < area->point_num; j++) {
            PR_DEBUG("    顶点[%d]: (%d, %d)", j,
                     area->point[j].x, area->point[j].y);
        }
        
        // TODO: 保存划区清扫配置
        // save_zone_clean_config(i, &p_zone_clean->clean_zone[i]);//伪代码,需要开发者自行实现
    }
    
    // 启动划区清扫任务
    // start_zone_clean_task(p_zone_clean->clean_zone, p_zone_clean->num);//伪代码,需要开发者自行实现
    
    // 处理完成后上报结果
    ZONE_CLEAN_S report_data;
    report_data.num = p_zone_clean->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID
    report_data.clean_zone = p_zone_clean->clean_zone;
    
    return ty_rvc_zone_clean_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_zone_clean_query(void)
{
    PR_DEBUG("收到划区清扫查询命令");
    
    // 从设备获取上次划区清扫配置
    ZONE_CLEAN_S query_data;
    ZONE_AREA_PARAM_S zones[5];     // 假设最多5个划区
    POINT_COOR_S zone_points[5][4]; // 每个划区4个顶点(矩形)
    
    query_data.num = get_last_zone_clean_count();  // 获取上次划区数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();      // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.clean_zone = zones;
    
    // 填充每个划区的数据
    for(int i = 0; i < query_data.num; i++) {
        // 填充划区名称
        zones[i].cur_name.name = "划区1";
        zones[i].cur_name.len = strlen(zones[i].cur_name.name);
        
        // 填充清扫模式(字符串格式)
        zones[i].mode.name = "both_work";  // 例如: "only_sweep","only_mop","both_work"
        zones[i].mode.len = strlen(zones[i].mode.name);
        
        // 填充清扫参数
        zones[i].param.suction.name = "normal";  // 例如: "closed","gentle","normal","strong","max"
        zones[i].param.suction.len = strlen(zones[i].param.suction.name);
        zones[i].param.cistern.name = "middle";  // 例如: "closed","low","middle","high"
        zones[i].param.cistern.len = strlen(zones[i].param.cistern.name);
        zones[i].param.y_mop = true;
        zones[i].param.clean_cnt = 2;
        
        // 填充划区区域坐标(矩形4个顶点)
        zones[i].area.point_num = 4;
        zones[i].area.point = zone_points[i];
        
        // 填充矩形顶点坐标(顺时针或逆时针)
        zone_points[i][0].x = 100; zone_points[i][0].y = 100;  // 左下
        zone_points[i][1].x = 300; zone_points[i][1].y = 100;  // 右下
        zone_points[i][2].x = 300; zone_points[i][2].y = 300;  // 右上
        zone_points[i][3].x = 100; zone_points[i][3].y = 300;  // 左上
    }
    
    return ty_rvc_zone_clean_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 划区形状
    • 通常为矩形区域(4个顶点)
    • 支持任意多边形,根据 point_num 确定顶点数量
    • 顶点坐标需按顺序排列(顺时针或逆时针)
  • 坐标系说明
    • 划区坐标使用机器坐标系(与虚拟墙、禁区相同)
    • 坐标类型:INT32 整数值,表示栅格数量
    • 实际位置计算:实际距离 = 栅格数量 × 栅格分辨率
    • 具体可参考地图坐标系说明
  • 字符串字段处理
    • modecur_namesuctioncistern 均为字符串格式
    • 每个字符串需要同时填充 name 指针和 len 长度
    • 字符串内存由应用管理,注意内存生命周期
  • 字符串转枚举
    • 清扫模式示例:"only_sweep" / "only_mop" / "both_work"
    • 吸力档位示例:"closed" / "gentle" / "normal" / "strong" / "max"
    • 水量档位示例:"closed" / "low" / "middle" / "high"
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据(包括所有数组和字符串)拷贝到应用自己分配的内存中
    • 设备上报(response接口):所有数组(buff及嵌套的坐标数组)及字符串内存(mode.namesuction.namecistern.namecur_name.name等)由应用自行分配和管理,SDK不会释放这些内存

定点清扫设置 & 查询

功能说明:定点清扫允许用户在地图上指定一个点位,机器人将清扫以该点为中心的固定区域(通常为1平方米)。

数据结构

```c
/**
* @brief Spot cleaning structure
*/
typedef struct {
    int num; // Number of spot cleanings
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    POINT_COOR_S* points; // List of spot coordinates, each spot area corresponds to one points element
    STR_ELEMENT_S* mode; // Cleaning mode for the zone, each area corresponds to one cleaning mode, mode[0] corresponds to point[0] area
    CLEAN_PARAM_S* param; // Cleaning parameters for selected areas, param[0] corresponds to point[0] area, if not set valid is 0
} SPOT_CLEAN_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收定点数量(通常为1) 填充上次定点清扫的数量 填充当前定点清扫的数量
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
points 坐标数组 解析定点坐标(中心点) 填充上次定点坐标 填充当前定点坐标
mode 字符串数组 解析清扫模式(字符串格式) 填充清扫模式字符串 填充清扫模式字符串
param 清扫参数数组 解析清扫参数 填充清扫参数 填充清扫参数
param[i].valid int 参数有效标志(备用字段) 填充有效标志 填充有效标志
param[i].suction 字符串 吸力档位(字符串格式) 填充吸力字符串 填充吸力字符串
param[i].cistern 字符串 水量档位(字符串格式) 填充水量字符串 填充水量字符串
param[i].y_mop bool Y字拖地开关 填充Y字拖开关 填充Y字拖开关
param[i].clean_cnt int 清扫次数 填充清扫次数 填充清扫次数

使用示例

// SET命令处理示例
static OPERATE_RET handle_spot_clean_set(SPOT_CLEAN_S* p_spot_clean)
{
    PR_DEBUG("收到定点清扫设置命令,定点数量: %d", p_spot_clean->num);
    
    // 通常定点清扫 num 固定为 1
    if(p_spot_clean->num != 1) {
        PR_WARN("定点清扫数量异常: %d,预期为1", p_spot_clean->num);
    }
    
    // 解析定点坐标
    for(int i = 0; i < p_spot_clean->num; i++) {
        PR_DEBUG("定点[%d] 坐标: (%d, %d)", i,
                 p_spot_clean->points[i].x,
                 p_spot_clean->points[i].y);
        
        // 解析清扫模式(字符串格式,需转换为枚举)
        if(p_spot_clean->mode) {
            PR_DEBUG("  清扫模式: %d-%s", 
                     p_spot_clean->mode[i].len,
                     p_spot_clean->mode[i].name);
            // TODO: 将字符串转换为枚举值
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_spot_clean->mode[i]);//伪代码,需要开发者自行实现
        }
        
        // 解析清扫参数
        PR_DEBUG("  吸力: %d-%s", 
                    p_spot_clean->param[i].suction.len,
                    p_spot_clean->param[i].suction.name);
        PR_DEBUG("  水量: %d-%s", 
                    p_spot_clean->param[i].cistern.len,
                    p_spot_clean->param[i].cistern.name);
        PR_DEBUG("  Y字拖: %d", p_spot_clean->param[i].y_mop);
        PR_DEBUG("  清扫次数: %d", p_spot_clean->param[i].clean_cnt);

        // TODO: 保存定点清扫配置
        // save_spot_clean_config(&p_spot_clean->points[i], ...);//伪代码,需要开发者自行实现
    }
    
    // 启动定点清扫任务(以该点为中心,清扫1平方米区域)
    // start_spot_clean_task(&p_spot_clean->points[0]);//伪代码,需要开发者自行实现
    
    // 处理完成后上报结果
    SPOT_CLEAN_S report_data;
    report_data.num = p_spot_clean->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.points = p_spot_clean->points;
    report_data.mode = p_spot_clean->mode;
    report_data.param = p_spot_clean->param;
    
    return ty_rvc_spot_clean_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_spot_clean_query(void)
{
    PR_DEBUG("收到定点清扫查询命令");
    
    // 从设备获取上次定点清扫配置
    SPOT_CLEAN_S query_data;
    POINT_COOR_S spot_point;        // 定点坐标
    STR_ELEMENT_S spot_mode;        // 清扫模式
    CLEAN_PARAM_S spot_param;       // 清扫参数
    
    // 定点清扫通常只有一个点
    query_data.num = 1;
    query_data.map_id = get_current_map_id();  // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.points = &spot_point;
    query_data.mode = &spot_mode;
    query_data.param = &spot_param;
    
    // 填充定点坐标(上次定点清扫的位置)
    spot_point.x = get_last_spot_x();  // 例如: 150,伪代码,需要开发者自行实现
    spot_point.y = get_last_spot_y();  // 例如: 200,伪代码,需要开发者自行实现
    
    // 填充清扫模式(字符串格式)
    spot_mode.name = "only_sweep";  // 例如: "only_sweep","only_mop","both_work"
    spot_mode.len = strlen(spot_mode.name);
    
    // 填充清扫参数(面板下发什么参数,即上报什么参数)
    spot_param.suction.name = "normal";  // 例如: "closed","gentle","normal","strong","max"
    spot_param.suction.len = strlen(spot_param.suction.name);
    spot_param.cistern.name = "middle";  // 例如: "closed","low","middle","high"
    spot_param.cistern.len = strlen(spot_param.cistern.name);
    spot_param.y_mop = false;
    spot_param.clean_cnt = 1;
    
    return ty_rvc_spot_clean_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 定点清扫特性
    • num 通常固定为 1,表示单个定点
    • 清扫区域:以 points 坐标为中心的 1 平方米区域
    • 清扫半径:通常为 0.5 米左右(根据实际项目调整)
  • 坐标系说明
    • 定点坐标使用机器坐标系(与虚拟墙、禁区相同)
    • 坐标类型:INT32 整数值,表示栅格数量
    • 实际位置计算:实际距离 = 栅格数量 × 栅格分辨率
    • 具体可参考地图坐标系说明
  • 字符串字段处理
    • modesuctioncistern 均为字符串格式
    • 每个字符串需要同时填充 name 指针和 len 长度
    • 字符串内存由应用管理,注意内存生命周期
  • 字符串转枚举
    • 清扫模式示例:"only_sweep" / "only_mop" / "both_work"
    • 吸力档位示例:"closed" / "gentle" / "normal" / "strong" / "max"
    • 水量档位示例:"closed" / "low" / "middle" / "high"
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据(包括所有数组和字符串)拷贝到应用自己分配的内存中
    • 设备上报(response接口):所有数组(buff)及字符串内存(mode.namesuction.namecistern.name等)由应用自行分配和管理,SDK不会释放这些内存

预约定时设置 & 查询

功能说明:预约定时允许用户设置定时清扫任务,支持单次执行或循环执行(按周循环),可为每个任务配置清扫房间和清扫参数。

数据结构

```c
/**
* @brief Scheduled time structure
*/
typedef struct {
    char hour; // Scheduled hour value
    char min; // Scheduled minute value
} SCHEDULE_TIME_S;

/**
* @brief Scheduled information structure
*/
typedef struct {
    unsigned char active; // Scheduled switch, 0 for off, 1 for on
    unsigned char cycle; // Whether to loop the schedule, value 0 indicates a one-time schedule, non-zero indicates a looping schedule, cycle's bit0~bit6 correspond to Monday~Sunday
    SCHEDULE_TIME_S start_time; // Scheduled effective time
    int num; // Number of rooms corresponding to each schedule
    bool customed_switch; // the switch of the clean param,when it is true,the clean param of the app send is unvalid
    int* ids; // Room ID categories corresponding to each schedule
    STR_ELEMENT_S* mode; // List of cleaning modes, mode[0] corresponds to ids[0]
    CLEAN_PARAM_S* param; // List of cleaning parameters, param[0] corresponds to ids[0]
} SCHEDULE_PARAM_S;
/**
* @brief Local schedule structure
*/
typedef struct {
    int num; // Number of schedules
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    SCHEDULE_PARAM_S* time_sets; // Parameter categories for each schedule
} SCHEDULE_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
num int 接收预约定时任务数量 填充当前所有预约任务数量 填充当前所有预约任务数量
map_id int 面板不下发,忽略该字段 必须填充当前地图ID 必须填充当前地图ID
time_sets 预约参数数组 解析所有预约任务参数 填充所有预约任务 填充所有预约任务
time_sets[i].active unsigned char 任务开关(0=关闭,1=开启) 填充任务开关状态 填充任务开关状态
time_sets[i].cycle unsigned char 循环模式(0=单次,非0=循环) 填充循环模式 填充循环模式
time_sets[i].start_time 时间结构 任务执行时间(时:分) 填充执行时间 填充执行时间
time_sets[i].num int 该任务要清扫的房间数量 填充房间数量 填充房间数量
time_sets[i].customed_switch bool 自定义参数开关 填充自定义开关 填充自定义开关
time_sets[i].ids int数组 要清扫的房间ID列表 填充房间ID 填充房间ID
time_sets[i].mode 字符串数组 各房间清扫模式(字符串) 填充清扫模式 填充清扫模式
time_sets[i].param 清扫参数数组 各房间清扫参数 填充清扫参数 填充清扫参数

使用示例

// SET命令处理示例
static OPERATE_RET handle_schedule_set(SCHEDULE_S* p_schedule)
{
    PR_DEBUG("收到预约定时设置命令,预约数量: %d", p_schedule->num);
    
    // 遍历所有预约任务
    for(int i = 0; i < p_schedule->num; i++) {
        SCHEDULE_PARAM_S* task = &p_schedule->time_sets[i];
        
        PR_DEBUG("预约任务[%d]:", i);
        PR_DEBUG("  开关状态: %d", task->active);
        PR_DEBUG("  执行时间: %02d:%02d", task->start_time.hour, task->start_time.min);
        
        // 解析循环模式
        if(task->cycle == 0) {
            PR_DEBUG("  执行模式: 单次执行");
        } else {
            PR_DEBUG("  执行模式: 循环执行");
            // cycle 的 bit0~bit6 对应周一~周日
            for(int day = 0; day < 7; day++) {
                if(task->cycle & (1 << day)) {
                    PR_DEBUG("    周%d", day + 1);
                }
            }
        }
        
        // 解析要清扫的房间
        PR_DEBUG("  清扫房间数: %d", task->num);
        for(int j = 0; j < task->num; j++) {
            PR_DEBUG("    房间ID[%d]: %d", j, task->ids[j]);
        }
        
        // 检查是否使用自定义清扫参数
        if(task->customed_switch) {
            PR_DEBUG("  使用自定义清扫参数:");
            
            // 解析各房间的清扫模式和参数
            for(int j = 0; j < task->num; j++) {
                if(task->mode) {
                    PR_DEBUG("    房间[%d] 清扫模式: %.*s", j,
                             task->mode[j].len, task->mode[j].name);
                }
                
                if(task->param) {
                    PR_DEBUG("    房间[%d] 吸力: %.*s", j,
                             task->param[j].suction.len,
                             task->param[j].suction.name);
                    PR_DEBUG("    房间[%d] 水量: %.*s", j,
                             task->param[j].cistern.len,
                             task->param[j].cistern.name);
                    PR_DEBUG("    房间[%d] Y字拖: %d", j, task->param[j].y_mop);
                    PR_DEBUG("    房间[%d] 清扫次数: %d", j, task->param[j].clean_cnt);
                }
            }
        } else {
            PR_DEBUG("  使用默认清扫参数");
        }
        
        // TODO: 保存预约任务到设备存储
        // save_schedule_task(i, task);//伪代码,需要开发者自行实现
    }
    
    // 处理完成后上报结果
    SCHEDULE_S report_data;
    report_data.num = p_schedule->num;
    report_data.map_id = get_current_map_id();  // 获取当前地图ID,伪代码,需要开发者自行实现
    report_data.time_sets = p_schedule->time_sets;
    
    return ty_rvc_schedule_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_schedule_query(void)
{
    PR_DEBUG("收到预约定时查询命令");
    
    // 从设备获取所有预约任务
    SCHEDULE_S query_data;
    SCHEDULE_PARAM_S tasks[10];     // 假设最多10个预约任务
    
    query_data.num = get_schedule_count();     // 获取预约任务数量,伪代码,需要开发者自行实现
    query_data.map_id = get_current_map_id();  // **必须填充地图ID**,伪代码,需要开发者自行实现
    query_data.time_sets = tasks;
    
    // 填充每个预约任务数据
    for(int i = 0; i < query_data.num; i++) {
        // 填充任务开关和时间
        tasks[i].active = 1;  // 1=开启
        tasks[i].start_time.hour = 9;   // 09:00
        tasks[i].start_time.min = 0;
        
        // 填充循环模式(bit0~bit6 对应周一~周日)
        tasks[i].cycle = 0x1F;  // 0b00011111 = 周一到周五
        
        // 填充要清扫的房间
        tasks[i].num = 2;  // 清扫2个房间
        
        // 分配并填充房间ID数组
        static int room_ids[10][10];  // 静态数组,实际应动态分配
        tasks[i].ids = room_ids[i];
        room_ids[i][0] = 1;  // 房间1
        room_ids[i][1] = 2;  // 房间2
        
        // 自定义参数开关
        tasks[i].customed_switch = true;
        
        // 分配并填充清扫模式数组
        static STR_ELEMENT_S modes[10][10];
        tasks[i].mode = modes[i];
        modes[i][0].name = "both_work";
        modes[i][0].len = strlen(modes[i][0].name);
        modes[i][1].name = "only_sweep";
        modes[i][1].len = strlen(modes[i][1].name);
        
        // 分配并填充清扫参数数组
        static CLEAN_PARAM_S params[10][10];
        tasks[i].param = params[i];
        for(int j = 0; j < tasks[i].num; j++) {
            params[i][j].suction.name = "strong";
            params[i][j].suction.len = strlen(params[i][j].suction.name);
            params[i][j].cistern.name = "high";
            params[i][j].cistern.len = strlen(params[i][j].cistern.name);
            params[i][j].y_mop = false;
            params[i][j].clean_cnt = 1;
        }
    }
    
    return ty_rvc_schedule_data_response(&query_data, 0);
}

注意事项

  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 循环模式说明
    • cycle = 0:单次执行(执行一次后自动失效)
    • cycle != 0:循环执行,bit0~bit6 对应周一~周日
    • 例如:cycle = 0x7F (0b01111111) 表示每天执行
    • 例如:cycle = 0x1F (0b00011111) 表示周一到周五执行
    • 例如:cycle = 0x60 (0b01100000) 表示周六和周日执行
  • 时间格式
    • start_time.hour:0-23(24小时制)
    • start_time.min:0-59
  • 自定义参数开关
    • customed_switch = false:使用设备默认清扫参数,忽略 modeparam 字段
    • customed_switch = true:使用面板下发的自定义清扫参数
  • 字符串字段处理
    • modesuctioncistern 均为字符串格式
    • 每个字符串需要同时填充 name 指针和 len 长度
  • 字符串转枚举
    • 清扫模式示例:"only_sweep" / "only_mop" / "both_work"
    • 吸力档位示例:"closed" / "gentle" / "normal" / "strong" / "max"
    • 水量档位示例:"closed" / "low" / "middle" / "high"
  • 数组索引对应关系
    • 所有数组索引与 ids 数组一一对应
    • ids[0] 对应 mode[0]param[0]
  • 内存管理
    • APP下发(SET命令):内存由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将数据(包括所有数组和字符串)拷贝到应用自己分配的内存中
    • 设备上报(response接口):所有数组(time_setsidsmodeparam等)及字符串内存(mode[i].namesuction.namecistern.name等)由应用自行分配和管理,SDK不会释放这些内存

勿扰定时设置 & 查询

功能说明:勿扰模式允许用户设置时间段,在该时间段内机器人如不会自动启动清扫任务(如预约定时任务),声音灯光关闭,避免打扰用户休息。

数据结构

```c
/**
* @brief Scheduled time structure
*/
typedef struct {
    char hour; // Scheduled hour value
    char min; // Scheduled minute value
} SCHEDULE_TIME_S;

/**
* @brief Do not disturb settings structure
*/
typedef struct {
    unsigned char active; // Do not disturb switch, 0 for off, 1 for on
    unsigned char other_day; // Effective time, 0 for today, 1 for the next day
    SCHEDULE_TIME_S start_time; // Do not disturb start time
    SCHEDULE_TIME_S end_time; // Do not disturb end time
} QUIET_HOURS_S;
```

结构体使用说明

字段 类型 SET命令处理 QUERY命令响应 主动上报
active unsigned char 勿扰模式开关(0=关闭,1=开启) 填充勿扰开关状态 填充勿扰开关状态
other_day unsigned char 结束时间日期(0=当天,1=次日) 填充日期标志 填充日期标志
start_time 时间结构 勿扰开始时间(时:分) 填充开始时间 填充开始时间
start_time.hour char 开始时间-小时(0-23) 填充小时 填充小时
start_time.min char 开始时间-分钟(0-59) 填充分钟 填充分钟
end_time 时间结构 勿扰结束时间(时:分) 填充结束时间 填充结束时间
end_time.hour char 结束时间-小时(0-23) 填充小时 填充小时
end_time.min char 结束时间-分钟(0-59) 填充分钟 填充分钟

使用示例

// SET命令处理示例
static OPERATE_RET handle_quiet_hours_set(QUIET_HOURS_S* p_quiet_hours)
{
    PR_DEBUG("收到勿扰定时设置命令");
    
    // 解析勿扰开关状态
    PR_DEBUG("勿扰模式: %s", p_quiet_hours->active ? "开启" : "关闭");
    
    // 解析勿扰时间段
    PR_DEBUG("开始时间: %02d:%02d", 
             p_quiet_hours->start_time.hour,
             p_quiet_hours->start_time.min);
    PR_DEBUG("结束时间: %02d:%02d %s", 
             p_quiet_hours->end_time.hour,
             p_quiet_hours->end_time.min,
             p_quiet_hours->other_day ? "(次日)" : "(当天)");
    
    // 判断跨天情况
    if(p_quiet_hours->other_day == 1) {
        // 跨天场景:例如 22:00 ~ 次日 08:00
        PR_DEBUG("勿扰时段跨天");
    } else {
        // 当天场景:例如 12:00 ~ 14:00
        PR_DEBUG("勿扰时段在同一天");
    }
    
    // 计算勿扰时长(示例)
    int start_minutes = p_quiet_hours->start_time.hour * 60 + p_quiet_hours->start_time.min;
    int end_minutes = p_quiet_hours->end_time.hour * 60 + p_quiet_hours->end_time.min;
    int duration;
    
    if(p_quiet_hours->other_day == 1) {
        // 跨天:从开始到午夜 + 午夜到结束
        duration = (24 * 60 - start_minutes) + end_minutes;
    } else {
        duration = end_minutes - start_minutes;
    }
    PR_DEBUG("勿扰时长: %d分钟 (%d小时%d分钟)", 
             duration, duration / 60, duration % 60);
    
    // TODO: 保存勿扰设置到设备存储
    // save_quiet_hours_config(p_quiet_hours);//伪代码,需要开发者自行实现
    
    // TODO: 启用/禁用勿扰模式
    if(p_quiet_hours->active) {
        // enable_quiet_mode(p_quiet_hours);//伪代码,需要开发者自行实现
    } else {
        // disable_quiet_mode();//伪代码,需要开发者自行实现
    }
    
    // 处理完成后上报结果
    QUIET_HOURS_S report_data = *p_quiet_hours;
    
    return ty_rvc_quiet_hours_data_response(&report_data, 0);  // 0表示成功
}

// QUERY命令响应示例
static OPERATE_RET handle_quiet_hours_query(void)
{
    PR_DEBUG("收到勿扰定时查询命令");
    
    // 从设备获取当前勿扰设置
    QUIET_HOURS_S query_data;
    
    // 填充勿扰开关状态
    query_data.active = get_quiet_mode_status();  // 0=关闭,1=开启,伪代码,需要开发者自行实现
    
    // 填充勿扰时间段
    query_data.start_time.hour = 22;  // 22:00
    query_data.start_time.min = 0;
    
    query_data.end_time.hour = 8;     // 08:00
    query_data.end_time.min = 0;
    
    // 判断是否跨天(结束时间早于开始时间则跨天)
    if(query_data.end_time.hour < query_data.start_time.hour ||
       (query_data.end_time.hour == query_data.start_time.hour &&
        query_data.end_time.min < query_data.start_time.min)) {
        query_data.other_day = 1;  // 跨天:22:00 ~ 次日 08:00
    } else {
        query_data.other_day = 0;  // 当天:12:00 ~ 14:00
    }
    
    return ty_rvc_quiet_hours_data_response(&query_data, 0);
}

注意事项

  • 勿扰模式作用
    • 勿扰期间,如设备不会自动执行预约定时任务,灯效声音关闭等
    • 勿扰期间,如用户可以手动启动清扫(App/遥控器/语音控制不受影响)等
    • 勿扰期间,设备可以正常充电和待机
    • 最终需要根据产品的需求来定义勿扰模式作用
  • 时间格式
    • hour:0-23(24小时制)
    • min:0-59
  • 跨天判断
    • other_day = 0:同一天,如 12:00 ~ 14:00
    • other_day = 1:跨天,如 22:00 ~ 次日 08:00
    • 判断逻辑:结束时间早于开始时间则为跨天
  • 常见场景示例
    • 午休勿扰:12:00 ~ 14:00 (other_day=0)
    • 夜间勿扰:22:00 ~ 次日 08:00 (other_day=1)
    • 全天勿扰:00:00 ~ 23:59 (other_day=0)
  • 实现建议
    • 设备需要实时判断当前时间是否在勿扰时段内
    • 勿扰开启时,禁止骚扰的任务自动触发
    • 勿扰关闭时,立即恢复
  • 结构体所有字段都会被使用,需要完整解析和上报

地图分区分割

功能说明:地图分区分割允许用户在地图上通过绘制分割线将一个房间分割成两个独立的房间,便于分区管理和清扫。

数据结构

```c
/**
* @brief Division line coordinate structure
*/
typedef struct {
    POINT_COOR_S points[2]; // Endpoints of the line
} DIVIDE_LINE_S;

/**
* @brief Manual partition structure
*/
typedef struct {
    int num; // Number of partitions to be divided
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    int* ids; // List of IDs to be divided
    DIVIDE_LINE_S* divi_line; // Partition division line coordinates, each room ID corresponds to one division line, divi_line[0] corresponds to ids[0]
} PART_DIVI_S;
```

结构体使用说明

字段 类型 SET命令处理 说明
num int 接收要分割的房间数量(当前仅支持1) 通常固定为1,表示单个房间分割
map_id int 面板不下发,忽略该字段 设备上报时必须填充当前地图ID
ids int数组 接收要分割的房间ID 当前仅支持单个房间ID
divi_line 分割线数组 接收分割线坐标 每个房间ID对应一条分割线
divi_line[i].points[0] 坐标 分割线起点坐标 (x0, y0) 分割线的第一个端点
divi_line[i].points[1] 坐标 分割线终点坐标 (x1, y1) 分割线的第二个端点

使用示例

// SET命令处理示例
static OPERATE_RET handle_part_division_set(PART_DIVI_S* p_part_divi)
{
    PR_DEBUG("收到地图分区分割命令,分割数量: %d", p_part_divi->num);
    
    // 当前仅支持单个房间分割
    if(p_part_divi->num != 1) {
        PR_ERR("不支持批量分割,当前仅支持单个房间分割");
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }
    
    // 解析要分割的房间ID
    int room_id = p_part_divi->ids[0];
    PR_DEBUG("要分割的房间ID: %d", room_id);
    
    // 检查房间ID是否有效
    if(!is_valid_room_id(room_id)) {  //伪代码,需要开发者自行实现
        PR_ERR("房间ID无效: %d", room_id);
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }
    
    // 解析分割线坐标
    DIVIDE_LINE_S* line = &p_part_divi->divi_line[0];
    PR_DEBUG("分割线起点: (%d, %d)", line->points[0].x, line->points[0].y);
    PR_DEBUG("分割线终点: (%d, %d)", line->points[1].x, line->points[1].y);
    
    // TODO: 执行房间分割算法
    // 1. 验证分割线是否穿过房间区域
    // 2. 计算分割后的两个新房间区域
    // 3. 更新地图数据结构
    // 4. 分配新的房间ID
    
    int result = perform_room_division(room_id, line);  //房间分割算法(伪代码),开发者自行实现
    if(result != 0) {
        PR_ERR("房间分割失败");
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }
    
    // 分割成功后上报结果
    PART_DIVI_S report_data;
    report_data.num = p_part_divi->num;
    report_data.map_id = get_current_map_id();  // **必须填充地图ID**
    report_data.ids = p_part_divi->ids;
    report_data.divi_line = p_part_divi->divi_line;
    
    return ty_rvc_part_division_data_response(&report_data, 0);
}

注意事项

  • 当前限制
    • 仅支持单个房间分割(num = 1
    • 不支持批量分割多个房间
  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 分割线要求
    • 分割线必须穿过房间区域内部
    • 分割线不能只触碰房间边界
    • 分割线应将房间分成两个有效区域
  • 坐标系说明
    • 分割线坐标使用机器坐标系
    • 坐标类型:INT32 整数值,表示栅格数量
    • 实际位置计算:实际距离 = 栅格数量 × 栅格分辨率
  • 分割后处理
    • 原房间ID失效,生成两个新的房间ID
    • 需要重新分配房间名称(如"房间1-1"、“房间1-2”)
    • 房间属性(清扫参数、清扫顺序等)可继承或重置
    • 需要更新地图数据并持久化保存
  • ⚠️ 必须上报房间属性
    • 分割操作完成后,必须立即调用 ty_rvc_room_property_data_response() 上报最新的房间属性
    • 上报内容应包括:分割后的所有房间ID、房间名称、清扫参数等完整信息
    • 如果不上报,App 面板将无法显示分割后的新房间信息
  • 响应状态码
    • 0:分割成功
    • -1:分割失败
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

地图分区合并

功能说明:地图分区合并允许用户将两个相邻的房间合并成一个房间,简化房间管理或修正过度分割的房间。

数据结构

```c
/**
* @brief Manual area merge structure
*/
typedef struct {
    int num; // Number of partitions to be merged
    int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    int* ids; // List of IDs to be merged
} PART_MERGE_S;
```

结构体使用说明

字段 类型 SET命令处理 说明
num int 接收要合并的房间数量(当前仅支持2) 通常固定为2,表示两个房间合并
map_id int 面板不下发,忽略该字段 设备上报时必须填充当前地图ID
ids int数组 接收要合并的房间ID列表 包含两个相邻房间的ID

使用示例

// SET命令处理示例
static OPERATE_RET handle_part_merge_set(PART_MERGE_S* p_part_merge)
{
    PR_DEBUG("收到地图分区合并命令,合并数量: %d", p_part_merge->num);
    
    // 当前仅支持两个房间合并
    if(p_part_merge->num != 2) {
        PR_ERR("不支持的合并数量,当前仅支持两个房间合并");
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }
    
    // 解析要合并的两个房间ID
    int room_id1 = p_part_merge->ids[0];
    int room_id2 = p_part_merge->ids[1];
    PR_DEBUG("要合并的房间: %d + %d", room_id1, room_id2);
    
    // 检查房间ID是否有效
    if(!is_valid_room_id(room_id1) || !is_valid_room_id(room_id2)) {  //伪代码,需要开发者自行实现
        PR_ERR("房间ID无效: %d 或 %d", room_id1, room_id2);
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }
    
    // 检查两个房间是否相邻
    if(!are_rooms_adjacent(room_id1, room_id2)) {  //伪代码,需要开发者自行实现
        PR_ERR("房间不相邻,无法合并: %d 和 %d", room_id1, room_id2);
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }
    
    // TODO: 执行房间合并算法
    // 1. 验证两个房间是否相邻(共享边界)
    // 2. 合并两个房间的区域
    // 3. 更新地图数据结构
    // 4. 分配新的房间ID或保留其中一个
    
    int result = perform_room_merge(room_id1, room_id2); //房间合并算法(伪代码),开发者自行实现
    if(result != 0) {
        PR_ERR("房间合并失败");
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }
    
    // 合并成功后上报结果
    PART_MERGE_S report_data;
    report_data.num = p_part_merge->num;
    report_data.map_id = get_current_map_id();  // **必须填充地图ID**,伪代码,需要开发者自行实现
    report_data.ids = p_part_merge->ids;
    
    return ty_rvc_part_merge_data_response(&report_data, 0);
}

注意事项

  • 当前限制
    • 仅支持两个房间合并(num = 2
    • 两个房间必须相邻(有共享边界)
    • 不支持合并不相邻的房间
  • 面板下发的 SET 命令中不包含 map_id 字段,设备需要自行判断当前地图
  • 设备上报时必须填充 map_id 字段,否则面板无法匹配地图
  • 相邻判断
    • 两个房间必须有共享边界(至少一个共享边界点)
    • 不能仅仅是角点接触,需要有边界重叠
    • 可以通过检查房间边界点是否有交集来判断
  • 合并后处理
    • 通常保留较小的房间ID,删除另一个
    • 合并后的房间名称可以继承或重新设置
    • 房间属性(清扫参数、清扫顺序等)可以继承保留房间的属性
    • 需要更新地图数据并持久化保存
  • ⚠️ 必须上报房间属性
    • 合并操作完成后,必须立即调用 ty_rvc_room_property_data_response() 上报最新的房间属性
    • 上报内容应包括:合并后的所有房间ID、房间名称、清扫参数等完整信息
    • 合并会导致房间数量减少,App 需要通过房间属性上报来更新显示
  • 常见失败原因
    • 房间ID不存在或无效
    • 两个房间不相邻
    • 合并数量不等于2
    • 合并算法执行失败
  • 响应状态码
    • 0:合并成功
    • -1:合并失败
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

地图分区恢复默认

功能说明:地图分区恢复默认用于将手动编辑过的房间信息恢复到初始默认状态,清除所有用户的分割、合并等房间信息操作。

数据结构

/**
 * @brief Manual area reset structure
 */
typedef struct {
    int map_id; // Current command operation corresponding map ID
} MAP_PART_RESET_S;

结构体使用说明

字段 类型 SET命令处理 说明
map_id int 恢复默认的地图ID(预留字段,面板不下发) 指定哪个地图需要恢复默认分区(预留功能)

使用示例

// SET命令处理示例
static OPERATE_RET handle_map_part_default_set(MAP_PART_RESET_S* p_part_reset)
{
    // 执行恢复默认分区操作
    // 1. 删除所有手动分割/合并的房间
    // 2. 重新加载系统自动分割的原始分区
    // 3. 重置房间ID和名称
    int map_id = get_current_map_id();  // **必须填充地图ID**,伪代码,需要开发者自行实现
    int ret = restore_default_partitions(map_id);  //恢复默认分区(伪代码),开发者自行实现
    if(ret != 0) {
        PR_ERR("恢复默认分区失败");
        return ty_rvc_map_part_default_data_response(p_part_reset, -1);
    }
    
    // 保存更新后的地图数据,开发者自行实现
    save_map_data(map_id);
    
    PR_DEBUG("地图分区恢复默认成功");
    
    return ty_rvc_map_part_default_data_response(p_part_reset, 0);
}

注意事项

  • 操作不可逆
    • 恢复默认后,所有手动分割和合并的记录将丢失
  • 影响范围
    • 删除所有手动分割的房间
    • 删除所有手动合并的房间
    • 恢复到设备首次建图时的自动分区状态
    • 房间名称恢复为默认名称(如"房间1"、“房间2”)
  • 房间属性
    • 房间的清扫参数、清扫顺序等属性会被重置
    • 用户自定义的房间名称会丢失
  • 必须上报房间属性
    • ⚠️ 恢复默认操作完成后,必须立即调用 ty_rvc_room_property_data_response() 上报恢复后的房间属性
    • 上报内容应包括:恢复后的所有房间ID、默认房间名称、重置后的清扫参数等
    • 这是一个重大变更操作,App 必须通过房间属性上报来刷新整个房间列表
  • 响应状态码
    • 0:恢复成功
    • -1:恢复失败
  • 使用场景
    • 用户对手动编辑不满意,想重新开始
    • 分区混乱,需要恢复初始状态
    • 测试或调试时快速重置
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

地图重置

功能说明:地图重置用于清除当前地图缓存的所有数据,设备将重新开始建图,适用于更换使用环境或地图数据损坏的情况。

数据结构

/**
 * @brief Current map reset structure
 */
typedef struct {
    int map_id; // Current command operation corresponding map ID
} CURRENT_MAP_RESET_S;

结构体使用说明

字段 类型 SET命令处理 说明
map_id int 重置的地图ID(预留字段,面板不下发) 指定哪个地图需要被清除(预留功能)

使用示例

// SET命令处理示例
static OPERATE_RET handle_map_reset_set(CURRENT_MAP_RESET_S* p_map_reset)
{
    // 检查是否为当前正在使用的地图
    int current_map_id = get_current_map_id();   //获取当前地图id(伪代码),需要开发者自行实现
    
    // 执行地图重置操作
    // 1. 清除地图数据
    // 2. 清除所有房间信息
    // 3. 清除虚拟墙、禁区等配置
    // 4. 清除清扫记录
    int ret = clear_map_data(current_map_id);   //删除首页地图数据(伪代码),需要开发者自行实现
    if(ret != 0) {
        PR_ERR("清除地图数据失败");
        return ty_rvc_map_clear_data_response(p_map_reset, -1);
    }
    
    PR_DEBUG("地图重置成功,准备重新建图");
    
    return ty_rvc_map_clear_data_response(p_map_reset, 0);
}

注意事项

  • 业务逻辑
    • 地图重置后,当前缓存地图将被清除,设备需上报一个空地图数据用于面板显示
    • 清除地图、房间、虚拟墙、禁区数据等缓存数据
    • 根据开发者自身的需求是否删除当前地图的内存中的数据(如果有)
  • 重新建图
    • 重置后设备将进入建图模式
    • 下次清扫时会重新构建地图
    • 新地图可能会分配新的map_id
  • ⚠️ 房间属性上报
    • 如果重置后重新建图产生了新的房间分区,必须调用 ty_rvc_room_property_data_response() 上报新的房间属性
    • 地图重置通常会清空所有房间信息,重新建图后如有房间需要上报
    • 如果地图被完全清空,可以上报空的房间列表(num = 0
  • 响应状态码
    • 0:重置成功
    • -1:重置失败
  • 使用场景
    • 更换使用环境(搬家等)
    • 地图数据损坏或异常
    • 首次清扫建图不理想,需要重建
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

地图保存

功能说明:地图保存用于将当前地图数据保存到本地或云端,支持多楼层地图管理,保存后的地图可以在不同楼层间切换使用。

数据结构

typedef enum {
    MAP_SAVE_LOCAL = 0, // Save map locally
    MAP_SAVE_CLOUD, // Save map to cloud
    MAP_SAVE_MAX,
} MAP_SAVE_MODE_E;

/**
 * @brief Map save structure
 */
typedef struct {
    MAP_SAVE_MODE_E save_mode; // Save mode
} MAP_SAVE_S;

结构体使用说明

字段 类型 SET命令处理 说明
save_mode 枚举 接收保存模式 指定保存到本地还是云端

保存模式说明

模式值 枚举名称 含义 说明
0 MAP_SAVE_LOCAL 本地保存 仅保存到设备本地存储
1 MAP_SAVE_CLOUD 云端保存(预留字段) 保存到云端,支持多设备同步

使用示例

// SET命令处理示例
static OPERATE_RET handle_map_save_set(MAP_SAVE_S* p_map_save)
{

    PR_DEBUG("收到地图保存命令,保存模式: %d", p_map_save->save_mode);
    
    // 检查保存模式是否有效
    if(p_map_save->save_mode >= MAP_SAVE_MAX) {
        PR_ERR("保存模式无效: %d", p_map_save->save_mode);
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    
    // 获取当前地图ID
    int current_map_id = get_current_map_id();     //获取当前地图ID(伪代码),开发者自行实现
    if(current_map_id <= 0) {
        PR_ERR("当前没有有效的地图");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    
    int ret = 0;

    // 保存到本地
    ret = save_map_to_local(current_map_id);  //保存本地(伪代码),开发者自行实现
    if(ret != 0) {
        PR_ERR("本地保存失败");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    // 如果需要保存到云端
    PR_DEBUG("保存地图到云端");
    // 然后上传到云端
    ret = upload_map_to_cloud(current_map_id);  //保存云端(伪代码),开发者自行实现
    if(ret != 0) {
        PR_ERR("云端上传失败");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    
    if(ret != 0) {
        PR_ERR("地图保存失败");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    
    PR_DEBUG("地图保存成功");
    
    return ty_rvc_map_save_data_response(p_map_save, 0);
}

注意事项

  • 保存模式
    • MAP_SAVE_LOCAL:仅本地保存,目前面板仅支持该模式,开发者如需要保存云端,收到该模式后,可自行上报云端。
    • MAP_SAVE_CLOUD:保存到云端,上报的接口可参考多楼层地图及清扫记录传输
  • 保存内容
    • 地图数据
    • 房间分区信息
    • 虚拟墙和禁区配置
    • 房间属性(名称、清扫参数等)
  • 多楼层支持
    • 每个楼层对应一个唯一的map_id
    • 云端保存支持多个楼层地图
    • 可在不同楼层间切换使用
  • 响应状态码
    • 0:保存成功
    • -1:保存失败
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

云端地图删除

功能说明:云端地图删除用于从云端删除指定的地图数据,同时删除本地对应的地图数据(如果有),通常用于删除不再使用的楼层地图。

数据结构

/**
 * @brief Delete cloud map structure
 */
typedef struct {
    unsigned int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
} DELETE_CLOUD_MAP_S;

结构体使用说明

字段 类型 SET命令处理 说明
map_id unsigned int 接收要删除的云端地图ID 指定要从云端删除的地图

使用示例

// SET命令处理示例
static OPERATE_RET handle_delete_cloud_map_set(DELETE_CLOUD_MAP_S* p_map_delete)
{

    PR_DEBUG("收到云端地图删除命令,云端地图ID: %d", p_map_delete->map_id);
    
    // 检查本地是否存在该地图
    if(!check_map_exists_in_cloud(p_map_delete->map_id)) {   //伪代码,开发者自行实现
        PR_ERR("本地不存在该地图: %d", p_map_delete->map_id);
        return ty_rvc_map_delete_data_response(p_map_delete, 0);
    }
    
    // 删除本地缓存(可选)
    ret = delete_local_map_cache(p_map_delete->map_id);
    if(ret != 0) {
        PR_ERR("本地地图删除失败");
        return ty_rvc_map_delete_data_response(p_map_delete, -1);
    }
    PR_DEBUG("地图删除成功");    //面板收到本地删除成功的回复之后,会自行去删除云端地图数据
    
    return ty_rvc_map_delete_data_response(p_map_delete, 0);
}

注意事项

  • 删除范围
    • 仅删除云端存储的地图数据
    • 本地地图数据可选择性保留或删除
    • 删除后无法从云端恢复
  • 响应状态码
    • 0:删除成功
    • -1:删除失败
  • 使用场景
    • 删除不再使用的楼层地图
    • 重新建图前清理旧地图
    • 更换使用环境
  • 与地图重置的区别
    • 地图重置:清除当前地图的缓存数据,准备重新建图
    • 云端地图删除:删除本地及云端存储的历史地图
  • 多楼层管理
    • 支持删除指定楼层的地图
    • 删除后该楼层需要重新建图
    • 其他楼层的地图不受影响
  • 这是一个仅用于SET命令的响应接口,无QUERY查询功能

语音查询

功能说明:语音查询用于查询设备当前使用的语音包状态,支持用户在App中切换不同语言的语音包。

数据结构

```c
/**
* @brief Voice download status
*/
typedef enum {
    RVC_DOWNLOAD_ST_FAILED = 0, // Voice download error
    RVC_DOWNLOAD_ST_LOADING, // Voice downloading
    RVC_DOWNLOAD_ST_SUCC, // Voice download successful
    RVC_DOWNLOAD_ST_USING // Voice in use
} VOICE_DOWNLOAD_ST_E;

/**
* @brief Use voice response structure
*/
typedef struct {
    unsigned int id; // Voice ID number
    VOICE_DOWNLOAD_ST_E download_status; // Voice download status
    int percent;  // Voice download progress percentage,range is 0~100
} USE_VOICE_LANGUAGE_RESPONSE_S; 
```

结构体使用说明

字段 类型 QUERY命令响应 主动上报
id unsigned int 填充当前使用的语音包ID 填充语音包ID
download_status 枚举 填充语音包下载状态 填充下载状态
percent int 填充下载进度(0-100) 填充下载进度

下载状态说明

状态值 枚举名称 含义 使用场景
0 RVC_DOWNLOAD_ST_FAILED 下载失败 语音包下载出错
1 RVC_DOWNLOAD_ST_LOADING 下载中 正在下载语音包
2 RVC_DOWNLOAD_ST_SUCC 下载成功 语音包下载完成但未启用
3 RVC_DOWNLOAD_ST_USING 使用中 当前正在使用的语音包

注意事项

  • SDK自动处理

    • SDK内部已经完成了语音包下载的工作
    • 开发者只需在设备初始化阶段调用 ty_rvc_voice_download_init 接口即可
    • SDK会自动处理语音包的下载和管理
  • 语音包ID

    • 不同的ID代表不同的语言(具体映射关系由项目定义)
    • 常见示例:1=中文、2=英文、3=德语、4=法语等
    • 需要与涂鸦平台配置的语音包ID保持一致
  • 进度上报

    • LOADING 状态:percent 应实时反映下载进度(0-99)
    • SUCC/USING 状态:percent 应为 100
    • FAILED 状态:percent 应为 0

使用地图功能

功能说明:地图功能支持多楼层地图管理,SDK自动处理楼层地图的下载到设备,由开发者根据进行地图切换。

使用方法

// 设备初始化时调用
OPERATE_RET ret = ty_rvc_map_download_init("伪代码-这里需要入参");
if(ret != OPRT_OK) {
    PR_ERR("地图下载初始化失败: %d", ret);
    return ret;
}
PR_DEBUG("地图下载功能初始化成功");

注意事项

  • SDK自动处理
    • 开发者只需在设备初始化阶段调用 ty_rvc_map_download_init 接口即可
    • SDK会自动处理地图的下载、存储和管理
  • 多楼层支持
    • 支持多个楼层地图的管理
    • 每个楼层有独立的地图ID(map_id)
    • SDK自动识别并下载新楼层的地图数据到本地
  • ⚠️ 必须上报房间属性
    • 使用地图功能涉及地图切换时,如果切换到的地图有房间信息,必须调用 ty_rvc_room_property_data_response() 上报该地图的房间属性
    • 地图下载完成后,如果包含房间分区信息,需要上报房间属性
    • 切换楼层地图时,需要上报新楼层的房间属性,确保App显示正确的房间列表
  • 实现建议
    • 在设备IOT SDK 初始化后调用该初始化接口
    • 确保设备已联网后再执行地图下载功能
    • 预留足够的存储空间用于多楼层地图数据
    • 地图切换时及时更新并上报房间属性
  • 业务端无需实现地图下载逻辑,SDK提供完整的自动化支持

密码验证

功能说明:密码验证用于校验用户输入的密码是否正确,验证通过后才能访问受保护的功能。

数据结构

typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

typedef struct {
    STR_ELEMENT_S password;
} PASSWORD_CHECK_S;

结构体使用说明

字段 类型 SET命令处理 说明
password 字符串 接收用户输入的密码 需要与存储的密码进行比对
password.name char* 密码字符串指针 实际密码内容
password.len int 密码字符串长度 密码的字节数

使用示例

// SET命令处理示例
static OPERATE_RET handle_password_check(PASSWORD_CHECK_S* p_password_check)
{
    if(NULL == p_password_check || NULL == p_password_check->password.name) {
        PR_ERR("密码验证参数无效");
        return ty_rvc_password_check_response(false, 0);
    }
    
    PR_DEBUG("收到密码验证请求,密码长度: %d", p_password_check->password.len);
    
    // 获取存储的密码
    char stored_password[32] = {0};
    int ret = read_password_from_storage(stored_password, sizeof(stored_password)); //伪代码,需要开发者自行实现
    
    if(ret != 0) {
        PR_ERR("读取密码失败");
        return ty_rvc_password_check_response(false, 0);
    }
    
    // 比对密码(注意:实际应用中建议使用加密哈希比对)
    if(p_password_check->password.len == strlen(stored_password) &&
       memcmp(p_password_check->password.name, stored_password, p_password_check->password.len) == 0) {
        PR_DEBUG("密码验证成功");
        return ty_rvc_password_check_response(true, 0);
    } else {
        PR_WARN("密码验证失败");
        return ty_rvc_password_check_response(true, 0);
    }
}

注意事项

  • 验证结果
    • true:密码正确
    • false:密码错误
  • 字符串处理
    • password 字段需要同时使用 namelen
    • 注意密码可能包含任意字符
  • 这是一个SET命令接口,用于验证操作
  • 内存管理
    • APP下发(SET命令):字符串内存(password.name)由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将字符串拷贝到应用自己分配的内存中

密码设置

功能说明:密码设置用于首次设置密码或修改已有密码,修改密码时需要提供旧密码验证。

数据结构

typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

typedef struct {
    STR_ELEMENT_S password;      // 新密码
    STR_ELEMENT_S old_password;  // 旧密码(修改时需要)
} PASSWORD_SET_S;

结构体使用说明

字段 类型 SET命令处理 说明
password 字符串 接收新密码 要设置的新密码
password.name char* 新密码字符串指针 新密码内容
password.len int 新密码字符串长度 新密码的字节数
old_password 字符串 接收旧密码 修改密码时需要,首次设置为空
old_password.name char* 旧密码字符串指针 旧密码内容
old_password.len int 旧密码字符串长度 旧密码的字节数

使用示例

// SET命令处理示例
static OPERATE_RET handle_password_set(PASSWORD_SET_S* p_password_set)
{
    if(NULL == p_password_set || NULL == p_password_set->password.name) {
        PR_ERR("密码设置参数无效");
        return ty_rvc_password_set_response(false, 0);
    }
    
    PR_DEBUG("收到密码设置请求,新密码长度: %d", p_password_set->password.len);
    
    // 检查当前是否已设置密码
    bool password_exists = is_password_configured();  //伪代码,需要开发者自行实现
    
    if(password_exists) {
        // 修改密码场景:需要验证旧密码
        if(NULL == p_password_set->old_password.name || p_password_set->old_password.len == 0) {
            PR_ERR("修改密码需要提供旧密码");
            return ty_rvc_password_set_response(false, 0);
        }
        
        // 验证旧密码
        char stored_password[32] = {0};
        int ret = read_password_from_storage(stored_password, sizeof(stored_password));  //伪代码,需要开发者自行实现
        
        if(ret != 0 || 
           p_password_set->old_password.len != strlen(stored_password) ||
           memcmp(p_password_set->old_password.name, stored_password, p_password_set->old_password.len) != 0) {
            PR_ERR("旧密码验证失败");
            return ty_rvc_password_set_response(false, 0);
        }
        
        PR_DEBUG("旧密码验证成功,开始更新密码");
    } else {
        // 首次设置密码场景
        PR_DEBUG("首次设置密码");
    }
    
    // 保存新密码(实际应用中应加密存储)
    char new_password[32] = {0};
    memcpy(new_password, p_password_set->password.name, p_password_set->password.len);
    new_password[p_password_set->password.len] = '\0';
    
    int ret = save_password_to_storage(new_password);  //伪代码,需要开发者自行实现
    if(ret != 0) {
        PR_ERR("保存密码失败");
        return ty_rvc_password_set_response(false, 0);
    }
    
    PR_DEBUG("密码设置成功");
    
    return ty_rvc_password_set_response(true, 0);
}

注意事项

  • 设置结果
    • true:密码设置成功
    • false:密码设置失败
  • 两种场景
    • 首次设置old_password 为空,直接设置新密码
    • 修改密码old_password 不为空,需要先验证旧密码
  • 字符串处理
    • passwordold_password 都需要使用 namelen
    • 注意密码可能包含任意字符
  • 这是一个SET命令接口,用于设置操作
  • 内存管理
    • APP下发(SET命令):字符串内存(password.nameold_password.name)由SDK管理,回调函数返回后SDK会自动释放。如果应用需要异步处理(如在其它线程中使用),必须先将字符串拷贝到应用自己分配的内存中

查询当前房间信息

功能说明:查询当前房间信息用于获取设备当前所在房间的位置信息,主要用于Alexa语音控制场景,注意涂鸦公版App面板不支持该功能。

数据结构

```c
typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

/**
* @brief  MCS Room info structure
*/
typedef struct {
    unsigned int map_id;  //Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
    unsigned int curr_name_id; //The current room ID where the machine is located.
    STR_ELEMENT_S curr_name;   //The current room name where the machine is located.
} MCS_ROOM_INFO_S;
```

结构体使用说明

字段 类型 QUERY命令响应 主动上报
map_id unsigned int 填充当前地图ID 填充当前地图ID
curr_name_id unsigned int 填充当前所在房间ID 填充当前所在房间ID
curr_name 字符串 填充当前所在房间名称 填充当前所在房间名称
curr_name.name char* 房间名称字符串指针 房间名称字符串指针
curr_name.len int 房间名称字符串长度 房间名称字符串长度

使用示例

// QUERY命令响应示例
static OPERATE_RET handle_mcs_room_info_query(void)
{
    PR_DEBUG("收到当前房间信息查询命令");
    
    // 准备上报数据
    MCS_ROOM_INFO_S room_info;
    
    // 填充当前地图ID
    room_info.map_id = get_current_map_id(); //伪代码,需要开发者自行实现
    
    // 填充当前所在房间ID
    room_info.curr_name_id = get_current_room_id(); //伪代码,需要开发者自行实现
    
    // 填充当前所在房间名称
    char* room_name = get_current_room_name();  // 例如: "客厅", "卧室", "厨房"
    if(room_name) {
        room_info.curr_name.name = room_name;
        room_info.curr_name.len = strlen(room_name);
    } else {
        // 如果无法获取房间名称,使用默认名称
        room_info.curr_name.name = "未知房间";
        room_info.curr_name.len = strlen("未知房间");
    }
    
    PR_DEBUG("当前位置: 地图ID=%d, 房间ID=%d, 房间名称=%.*s",
             room_info.map_id,
             room_info.curr_name_id,
             room_info.curr_name.len,
             room_info.curr_name.name);
    
    return ty_rvc_mcs_room_info_response(&room_info, 0);  // 0表示查询成功
}

注意事项

  • 使用场景
    • 主要用于Alexa语音控制场景
    • Alexa需要知道设备当前位置来执行特定房间的清扫命令
    • 涂鸦公版App面板不支持该功能
  • 字符串字段处理
    • curr_name 需要同时填充 name 指针和 len 长度
    • 房间名称应使用用户自定义的名称(如"客厅"、“卧室”)
    • 字符串内存由应用管理,注意内存生命周期
  • 房间定位方法
    • 通过机器人当前坐标判断所在房间
    • 检查坐标是否在某个房间的区域范围内
    • 使用点在多边形内的算法进行判断
  • 主动上报时机
    • 机器人从一个房间移动到另一个房间时
    • 清扫任务开始时(上报起始房间)
    • Alexa语音控制触发查询时
  • 数据同步
    • 房间名称应与房间属性设置中的名称保持一致
    • map_id 必须填充当前地图ID
    • curr_name_id 应为有效的房间ID
  • 这是一个QUERY查询接口,也支持设备主动上报位置变化

流程说明

  • 设备端与涂鸦 App 进行数据同步有以下三种方式:
    • 设备端数据状态变化时,主动调用接口进行数据上报。
    • 面板端发起数据查询命令,设备端进行被动响应完成数据上报。
    • 面板端发起数据设置命令,设备端完成相应功能处理后,调用接口进行数据上报同步。
  • 应用收到 FUNC_SET_CB 回调对应的处理命令后,需要异步处理该事件,以保证频繁点击面板进行命令下发时,不对后续命令解析造成阻塞,异步处理时需要将命令参数先进行数据复制,然后使用复制后的数据进行命令事件处理。
应用TuyaOS_SDK涂鸦 App设备上电数据命令设置/查询回调函数注册到-接口:ty_rvc_advance_func_reg-ister设备联网成功通过ty_rvc_device_info_data_respon-se 等 response接口设备端进行数据上报数据上报,完成与云端的数据同步用户进行面板 UI 交互,下发命令设置参数SDK 进行数据解析为保证不对命令解析造成后续阻塞,建议应用对命令进行异步处理异步处理时需要将命令参数先进行数据复制,然后使用复制后的数据进行命令事件处-SDK调用数据命令设置回调函数进行数-据处理设备端完成命令设置功能处理后,-调用 response 接口进行数据上报数据上报,完成与云端的数据同步用户进行面板 UI 交互,下发命令查询参数SDK 进行数据解析SDK调用数据命令查询回调函数进行数-据处理设备端完成命令查询功能处理后,-调用 response 接口进行数据上报数据上报,完成与云端的数据同步应用TuyaOS_SDK涂鸦 App

API

协议处理回调函数注册

命令处理回调函数的注册接口。回调函数注册成功后,SDK 在收到云端下发的命令协议并成功解析后会调用注册的回调函数执行业务功能。需要注意的是回调函数中的 param 入参变量会在回调函数被调用后释放掉,如果回调函数有异步操作,需要将该入参的参数数据复制到应用自己申请的变量空间中。

/**
 * @brief Sweeper advanced function setting callback function pointer
 * @param [ADVANCE_CMD_E] cmd, protocol command, the SDK uses this parameter to notify the application of the current setting command to be processed, see the list for setting commands, all ending with SET
 * @param [void *] param, parameters corresponding to the command, the application converts the param parameter into the command corresponding parameter structure based on the cmd command field, the structure correspondence can refer to the accompanying demo
 */
typedef OPERATE_RET (*FUNC_SET_CB)(IN ADVANCE_CMD_E cmd, IN void* param);

/**
 * @brief Sweeper advanced function query callback function pointer
 * @param [ADVANCE_CMD_E] cmd, protocol command, the SDK uses this parameter to notify the application of the current query command to be processed, see the list for query commands, all ending with QUERY
 * @param [void *] param, parameters corresponding to the command, current query command, its parameters are meaningless and do not need to be processed
 * @note  When the application gets the corresponding command parameters in the query callback function and needs to report, please call the corresponding response reporting function, for example, after processing the VIRTUAL_WALL_QUERY command, when needing to report parameters, use the ty_rvc_virtual_wall_data_response interface to report
 */
typedef OPERATE_RET (*FUNC_QUERY_CB)(IN ADVANCE_CMD_E cmd, IN void* param);

/**
 * @brief Register advanced capability setting and query callback functions
 * @param[in] sets_handler: Command setting callback function that the application needs to implement
 * @param[in] query_handler: Command query callback function that the application needs to implement
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_advance_func_register(IN FUNC_SET_CB sets_handler, IN FUNC_QUERY_CB query_handler);

参数 说明
sets_handler 入参,设置命令的处理回调函数,由业务应用实现。通过该接口注册到 SDK,回调函数的 param 入参为命令参数,应用应根据命令类型将该参数转换成对应的命令参数结构体,再进行使用
query_handler 入参,查询命令的处理回调函数,由业务应用实现。通过该接口注册到 SDK,回调函数的 param 入参当前未使用,应用无需关心
OPERATE_RET 返回值,OPRT_OK 代表执行成功,其它值代表执行失败,错误码含义参见 TuyaOS_SDK 对应头文件定义

数据上报接口

设备端数据上报接口,用于完成与涂鸦 App 的数据同步,各接口完成的数据上报功能列表如下:

接口名称 接口功能 备注
ty_rvc_virtual_wall_data_response 虚拟墙数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_restricted_area_data_response 禁区数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_room_property_data_response 房间属性数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_room_clean_data_response 选区清扫数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_spot_clean_data_response 定点清扫数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_zone_clean_data_response 划区清扫数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_schedule_data_response 预约定时数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_quiet_hours_data_response 勿扰定时数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_voice_language_data_response 语种切换数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_device_info_data_response 设备信息数据上报 设备端有相应数据变化时应主动调用该接口进行数据上报
ty_rvc_part_division_data_response 地图分区分割设置结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_part_merge_data_response 地图分区合并设置结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_map_part_default_data_response 地图分区恢复默认数据上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_map_clear_data_response 地图重置命令设置结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_map_save_data_response 地图保存命令设置结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_map_delete_data_response 地图删除命令设置结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_map_empty_response 空地图查询命令结果上报 该功能仅用于查询命令执行后的结果回复,面板无查询命令下发时设备无需调用该接口进行数据同步
ty_rvc_password_check_response 密码验证命令结果上报 该功能仅用于查询命令执行后的结果回复,面板无查询命令下发时设备无需调用该接口进行数据同步
ty_rvc_password_state_response 密码状态命令结果上报 该功能仅用于设置或者查询命令执行后的结果回复,面板无设置或者查询命令下发时设备无需调用该接口进行数据同步
ty_rvc_password_set_response 设置密码命令结果上报 该功能仅用于设置命令执行后的结果回复,面板无设置命令下发时设备无需调用该接口进行数据同步
ty_rvc_mcs_room_info_response 查询当前房间信息结果上报 设备端有相应数据变化时应主动调用该接口进行数据上报

/**
 * @brief Report virtual wall information
 * @param[in] p_virtual_wall: Virtual wall data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Command execution result, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_virtual_wall_data_response(VIRTUAL_WALL_S* p_virtual_wall, int errcode);

/**
 * @brief Report restricted area data
 * @param[in] p_restricted_area: Restricted area data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_restricted_area_data_response(RESTRICTED_AREA_S* p_restricted_area, int errcode);

/**
 * @brief Report room property data
 * @param[in] p_room_clean: Room property data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_room_property_data_response(ROOM_PROPERTY_S* p_room_clean, int errcode);

/**
 * @brief Report selected area cleaning parameter values
 * @param[in] p_room_clean: Cleaning parameter input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, no error, input parameter is 0
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_room_clean_data_response(ROOM_CLEAN_S* p_room_clean, int errcode);

/**
 * @brief Report spot cleaning data
 * @param[in] spot_clean_data: Spot cleaning data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_spot_clean_data_response(SPOT_CLEAN_S* spot_clean_data, int errcode);

/**
 * @brief Report zone cleaning data
 * @param[in] p_zone_area: Zone cleaning data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_zone_clean_data_response(ZONE_CLEAN_S* p_zone_area, int errcode);

/**
 * @brief Partition setting reply
 * @param[in] p_part_division: Partition parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_part_division_data_response(PART_DIVI_S* p_part_division, PART_DIV_ST_E errcode);

/**
 * @brief Partition merge command reply
 * @param[in] p_part_merge: Partition setting reply parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_part_merge_data_response(PART_MERGE_S* p_part_merge, PART_MERGE_ST_E errcode);

/**
 * @brief Partition restore default command response
 * @param[in] p_part_merge: Structure parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_part_default_data_response(MAP_PART_RESET_S* p_part_merge, PART_RESET_ST_E errcode);

/**
 * @brief Clear home map reply
 * @param[in] p_map_reset: Map reset structure, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_clear_data_response(CURRENT_MAP_RESET_S* p_map_reset, MAP_RESET_ST_E errcode);

/**
 * @brief Map save command reply
 * @param[in] p_map_save: Map save reply parameter input, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_save_data_response(MAP_SAVE_S* p_map_save, MAP_SAVE_ST_E errcode);

/**
 * @brief Map delete command reply
 * @param[in] p_map_delete: Map delete reply parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_delete_data_response(DELETE_CLOUD_MAP_S* p_map_delete, MAP_DELETE_ST_E errcode);

/**
 * @brief Scheduled data report
 * @param[in] p_schedule: Scheduled data, passed in by the application, parameters are defined in the structure
 * @param[in] current_map_id:
 * @param[in] errcode:
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_schedule_data_response(SCHEDULE_S* p_schedule, int errcode);

/**
 * @brief Do not disturb scheduled data report
 * @param[in] p_quiet_hours: Do not disturb scheduled data input, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_quiet_hours_data_response(QUIET_HOURS_S* p_quiet_hours, int errcode);

/**
 * @brief Voice use parameter report
 * @param[in] p_voice_language_res: Voice use parameter report, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_voice_language_data_response(USE_VOICE_LANGUAGE_RESPONSE_S* p_voice_language_res, VOICE_USE_ST_E errcode);

/**
 * @brief Device information report, info field is concatenated by the customer in key:value format in JSON, when the app panel displays device information, it shows each value in the order of the key list
 * The panel needs to correctly configure multilingual support for each key value
 * This interface is responsible for completing data reporting via the MQTT channel
 *
 * @param[char*] info: Application's JSON format string
 * @param [int] len: String length
 * @return OPERATE_RET, OPRT_OK indicates success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_device_info_data_response(char* info, int len);            

/**
 * @brief Report dev room info
 * @param[in] room_info: Room information parameters, passed in by the application, are defined in the structure.
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_mcs_room_info_response(MCS_ROOM_INFO_S* room_info, int errcode);      
           

使用示例


static OPERATE_RET __sweeper_advance_function_set(OUT ADVANCE_CMD_E cmd, OUT void *param)
{
    OPERATE_RET ret = 0;
    int i=0,j=0;
    switch (cmd) {
        case VIRTUAL_WALL_SET: {
            VIRTUAL_WALL_S* p_virtual_wall = (VIRTUAL_WALL_S*)param;
            PR_DEBUG("virtual wall num:%d", p_virtual_wall->num);
            for(i = 0; i < p_virtual_wall->num;i++){
                PR_DEBUG("mode:%d", p_virtual_wall->line[i].mode);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[0].x);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[0].y);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[1].x);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[1].y);
            }

            ........
            //此处仅演示接口用法,实际使用时应在异步事件处理函数中获取设备实际数据,并进行数据上报
            current_map_id = 8;
            errcode = 0;
            ty_rvc_virtual_wall_data_response(p_virtual_wall, current_map_id, errcode);  

            }break;
            .......
            default:
                PR_DEBUG("cmd not support now");
                break;
    }

}


static OPERATE_RET __sweeper_advance_function_query(OUT ADVANCE_CMD_E cmd, OUT void *param)
{
    OPERATE_RET ret = OPRT_OK;
    uint16_t cmd_index = 0;
    switch(cmd){
        case VIRTUAL_WALL_QUERY:{
			// 使用测试数据进行数据上报演示
            VIRTUAL_WALL_S test_virtual_wall;
            VIRTUAL_LINE_S virtual_line[10] = {0};
            OPERATE_RET ret = OPRT_OK;

            test_virtual_wall.num = 1;
            for (int i = 0; i < test_virtual_wall.num; i++) {
                virtual_line[i].mode = FORBIT_ALL;
                virtual_line[i].points[0].x = 100;
                virtual_line[i].points[0].y = 200;
                virtual_line[i].points[1].x = 50;
                virtual_line[i].points[1].y = 60;
            }
            test_virtual_wall.line = virtual_line;
            int current_map_id = 7;
            ty_rvc_virtual_wall_data_response(&test_virtual_wall, current_map_id, errcode);          
        }break;
        default:
        PR_DEBUG("cmd not support now");
        break;
    }
    return ret;
}


// 上电主流程
int main(int argc, char* argv[])
{
    OPERATE_RET ret = 0;
    ....
    ret = ty_sys_start();
    if (ret != OPRT_OK) {
        PR_ERR("[%s, %d] sys start failed", __FUNCTION__, __LINE__);
        return ret;
    }

    ty_rvc_advance_func_register(__sweeper_advance_function_set, __sweeper_advance_function_query);
	....
}