endpointhash 1.0

This commit is contained in:
flower_linux
2026-04-16 10:39:56 +08:00
parent 11bd86063d
commit 11bc015444
17 changed files with 885 additions and 38 deletions

View File

@@ -1,8 +1,9 @@
# softbus_daemon API 接口表
本文档汇总 `src/api/ipc_dbus` 当前对外接口,分为:
本文档汇总 `src/api` 当前对外接口,分为:
- D-Bus 直接方法
- `executeCommand` JSON 命令接口
- HTTP REST 接口
## 1) D-Bus 服务基础信息
@@ -53,15 +54,49 @@
| action | params 关键字段 | 成功 code | 失败 code | 说明 |
|---|---|---:|---:|---|
| `add_filter` | `id`, `endpoint?`, `deviceId?`, `stableKey?`, `condition?` | `0` | `4001` | 添加过滤规则 |
| `add_filter` | `id`, `endpointHash?`, `deviceId?`, `stableKey?`, `condition?` | `0` | `4001` | 添加过滤规则 |
| `remove_filter` | `id` | `0` | `4041` | 删除过滤规则 |
| `add_transform_rule` | `id`, `endpoint?`, `deviceId?`, `stableKey?`, `scale?`, `offset?`, `unit?` | `0` | `4002` | 添加转换规则 |
| `add_transform_rule` | `id`, `endpointHash?`, `deviceId?`, `stableKey?`, `scale?`, `offset?`, `unit?` | `0` | `4002` | 添加转换规则 |
| `remove_transform_rule` | `id` | `0` | `4042` | 删除转换规则 |
| `add_mapping_rule` | `compositeKey`, `domPath`, `scale?`, `offset?`, `unit?` | `0` | `4004` | 新增/更新映射规则 |
| `remove_mapping_rule` | `compositeKey` | `0` | `4043` | 删除映射规则 |
| `list_mapping_rules` | 无 | `0` | - | 返回映射规则列表 |
| `discovery_start` | `ttlMs?` | `0` | - | 开启嗅探模式 |
| `discovery_stop` | 无 | `0` | - | 关闭嗅探模式 |
| `discovery_clear` | 无 | `0` | - | 清空发现池 |
| `list_rules` | 无 | `0` | - | 返回规则计数状态 |
| `get_status` | 无 | `0` | - | 返回规则计数状态 |
| `get_status` | 无 | `0` | - | 返回规则计数 + discovery/mapping 状态 |
| 其他未知 action | - | - | `4040` | 未知动作 |
## 5) 通用错误码约定(当前实现
## 5) HTTP REST 路由表RestApiRoutes
### 5.1 通用路由
| 方法 | 路径 | 说明 |
|---|---|---|
| `GET` | `/health` | 健康检查,返回 `{"ok": true}` |
| `GET` | `/v1/status` | 透传 `get_status` 的状态响应 |
| `POST` | `/v1/command` | 统一命令入口(请求体为 executeCommand JSON |
### 5.2 Discovery 路由
| 方法 | 路径 | 请求参数 | 说明 |
|---|---|---|---|
| `POST` | `/api/v1/discovery/start` | body: `ttlMs?` | 开启嗅探;可设置自动关闭 TTL |
| `POST` | `/api/v1/discovery/stop` | 无 | 关闭嗅探 |
| `GET` | `/api/v1/discovery/status` | 无 | 获取发现池运行状态 |
| `POST` | `/api/v1/discovery/clear` | 无 | 清空发现池样本 |
| `GET` | `/api/v1/discovery/samples` | query: `offset?`, `limit?`, `endpointHash?`, `protocol?` | 拉取样本(按更新时间倒序,且过滤已映射 key |
### 5.3 Mapping 路由
| 方法 | 路径 | 请求参数 | 说明 |
|---|---|---|---|
| `POST` | `/api/v1/mapping/bind` | body: `compositeKey`, `domPath`, `scale?`, `offset?`, `unit?` | 绑定或更新映射规则 |
| `POST` | `/api/v1/mapping/unbind` | body: `compositeKey` | 删除映射规则 |
| `GET` | `/api/v1/mapping/list` | 无 | 列出当前映射规则 |
## 6) 通用错误码约定(当前实现)
| code | message | 说明 |
|---:|---|---|
@@ -70,14 +105,19 @@
| `4001` | `invalid_filter_rule` | 过滤规则参数非法 |
| `4002` | `invalid_transform_rule` | 转换规则参数非法 |
| `4003` | `invalid_action` | action 为空或分发器不可用 |
| `4004` | `invalid_mapping_rule` | 映射规则参数非法 |
| `4040` | `unknown_action` | action 未注册 |
| `4041` | `filter_not_found` | 删除过滤规则时未找到 |
| `4042` | `transform_rule_not_found` | 删除转换规则时未找到 |
| `4043` | `mapping_rule_not_found` | 删除映射规则时未找到 |
## 6) 维护建议
## 7) 维护建议
- 新增 action 时,同时更新:
- `CommandDispatcher::dispatch(...)`
- 本文档第 4 节 action 表
- 新增 REST 路由时,同时更新:
- `RestApiRoutes.cpp`
- 本文档第 5 节路由表
- 若扩展返回结构,保持 `code/message/data` 三元组不变,避免客户端兼容性问题。

View File

@@ -1,18 +1,164 @@
#include "api/http/RestApiRoutes.h"
#include <algorithm>
#include <QHttpServer>
#include <QHttpServerRequest>
#include <QHttpServerResponse>
#include <QHttpServerResponder>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QUrlQuery>
#include <utility>
#include "api/http/RestApiCommandHttp.h"
#include "api/ipc_dbus/CommandDispatcher.h"
#include "message_bus/pipeline/discovery/DiscoveryPool.h"
#include "message_bus/pipeline/mapping/MappingRegistry.h"
namespace softbus::api::http
{
namespace
{
softbus::core::models::ProtocolType protocolFromString(const QString& s)
{
const QString x = s.trimmed().toUpper();
if (x == QStringLiteral("MODBUS_RTU")) {
return softbus::core::models::ProtocolType::MODBUS_RTU;
}
if (x == QStringLiteral("MODBUS_ASCII")) {
return softbus::core::models::ProtocolType::MODBUS_ASCII;
}
if (x == QStringLiteral("MODBUS_TCP")) {
return softbus::core::models::ProtocolType::MODBUS_TCP;
}
if (x == QStringLiteral("CAN_RAW")) {
return softbus::core::models::ProtocolType::CAN_RAW;
}
if (x == QStringLiteral("CAN_OPEN")) {
return softbus::core::models::ProtocolType::CAN_OPEN;
}
if (x == QStringLiteral("UDP_CUSTOM")) {
return softbus::core::models::ProtocolType::UDP_CUSTOM;
}
return softbus::core::models::ProtocolType::UNKNOWN;
}
QJsonObject parseJsonBody(const QHttpServerRequest& request)
{
QJsonParseError err;
const auto doc = QJsonDocument::fromJson(request.body(), &err);
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
return {};
}
return doc.object();
}
void registerDiscoveryRoutes(QHttpServer* server, QObject* routeContext)
{
server->route(QStringLiteral("/api/v1/discovery/start"), QHttpServerRequest::Method::Post, routeContext,
[](const QHttpServerRequest& request) {
auto& pool = softbus::message_bus::pipeline::DiscoveryPool::instance();
const auto payload = parseJsonBody(request);
const std::int64_t ttlMs = payload.value(QStringLiteral("ttlMs")).toInteger(0);
pool.startSniffing(ttlMs);
return QHttpServerResponse(pool.status(), QHttpServerResponse::StatusCode::Ok);
});
server->route(QStringLiteral("/api/v1/discovery/stop"), QHttpServerRequest::Method::Post, routeContext, []() {
auto& pool = softbus::message_bus::pipeline::DiscoveryPool::instance();
pool.stopSniffing();
return QHttpServerResponse(pool.status(), QHttpServerResponse::StatusCode::Ok);
});
server->route(QStringLiteral("/api/v1/discovery/status"), QHttpServerRequest::Method::Get, routeContext, []() {
auto& pool = softbus::message_bus::pipeline::DiscoveryPool::instance();
pool.tick();
return QHttpServerResponse(pool.status(), QHttpServerResponse::StatusCode::Ok);
});
server->route(QStringLiteral("/api/v1/discovery/clear"), QHttpServerRequest::Method::Post, routeContext, []() {
auto& pool = softbus::message_bus::pipeline::DiscoveryPool::instance();
pool.clear();
return QHttpServerResponse(pool.status(), QHttpServerResponse::StatusCode::Ok);
});
server->route(QStringLiteral("/api/v1/discovery/samples"), QHttpServerRequest::Method::Get, routeContext,
[](const QHttpServerRequest& request) {
auto& pool = softbus::message_bus::pipeline::DiscoveryPool::instance();
auto& mappingReg = softbus::message_bus::pipeline::MappingRegistry::instance();
pool.tick();
const QUrlQuery query(request.url().query());
const std::size_t offset =
static_cast<std::size_t>(std::max(0, query.queryItemValue(QStringLiteral("offset")).toInt(0)));
bool limitOk = false;
const int limitCandidate = query.queryItemValue(QStringLiteral("limit")).toInt(&limitOk, 10);
const int limitRaw = limitOk ? limitCandidate : 200;
const std::size_t limit = static_cast<std::size_t>(std::clamp(limitRaw, 1, 500));
const std::uint32_t endpointHash = static_cast<std::uint32_t>(
query.queryItemValue(QStringLiteral("endpointHash")).toUInt(nullptr, 10));
const auto protocol = protocolFromString(query.queryItemValue(QStringLiteral("protocol")));
QJsonObject o;
o.insert(QStringLiteral("items"),
pool.listSamples(offset, limit, endpointHash, protocol, mappingReg.snapshotMappedKeys()));
o.insert(QStringLiteral("offset"), static_cast<qint64>(offset));
o.insert(QStringLiteral("limit"), static_cast<qint64>(limit));
o.insert(QStringLiteral("status"), pool.status());
return QHttpServerResponse(o, QHttpServerResponse::StatusCode::Ok);
});
}
void registerMappingRoutes(QHttpServer* server, QObject* routeContext)
{
server->route(QStringLiteral("/api/v1/mapping/bind"), QHttpServerRequest::Method::Post, routeContext,
[](const QHttpServerRequest& request) {
const auto payload = parseJsonBody(request);
softbus::message_bus::pipeline::MappingRule rule;
rule.compositeKey = payload.value(QStringLiteral("compositeKey")).toString().toULongLong();
rule.domPath = payload.value(QStringLiteral("domPath")).toString();
rule.scale = payload.value(QStringLiteral("scale")).toDouble(1.0);
rule.offset = payload.value(QStringLiteral("offset")).toDouble(0.0);
rule.unit = payload.value(QStringLiteral("unit")).toString();
auto& mappingReg = softbus::message_bus::pipeline::MappingRegistry::instance();
if (!mappingReg.upsert(rule)) {
return QHttpServerResponse(
QJsonObject{{QStringLiteral("error"), QStringLiteral("invalid_mapping_rule")}},
QHttpServerResponse::StatusCode::BadRequest);
}
return QHttpServerResponse(QJsonObject{{QStringLiteral("ok"), true},
{QStringLiteral("compositeKey"),
QString::number(rule.compositeKey)}},
QHttpServerResponse::StatusCode::Ok);
});
server->route(QStringLiteral("/api/v1/mapping/unbind"), QHttpServerRequest::Method::Post, routeContext,
[](const QHttpServerRequest& request) {
const auto payload = parseJsonBody(request);
const std::uint64_t compositeKey =
payload.value(QStringLiteral("compositeKey")).toString().toULongLong();
auto& mappingReg = softbus::message_bus::pipeline::MappingRegistry::instance();
const bool ok = mappingReg.remove(compositeKey);
return QHttpServerResponse(QJsonObject{{QStringLiteral("ok"), ok},
{QStringLiteral("compositeKey"),
QString::number(compositeKey)}},
ok ? QHttpServerResponse::StatusCode::Ok
: QHttpServerResponse::StatusCode::NotFound);
});
server->route(QStringLiteral("/api/v1/mapping/list"), QHttpServerRequest::Method::Get, routeContext, []() {
auto& mappingReg = softbus::message_bus::pipeline::MappingRegistry::instance();
return QHttpServerResponse(QJsonObject{{QStringLiteral("items"), mappingReg.list()}},
QHttpServerResponse::StatusCode::Ok);
});
}
} // namespace
void registerSoftbusRestApiRoutes(QHttpServer* server,
QObject* routeContext,
softbus::api::ipc::CommandDispatcher* dispatcher)
@@ -33,6 +179,8 @@ void registerSoftbusRestApiRoutes(QHttpServer* server,
[dispatcher](const QHttpServerRequest& request) {
return handleExecuteCommandHttpBody(request.body(), *dispatcher);
});
registerDiscoveryRoutes(server, routeContext);
registerMappingRoutes(server, routeContext);
server->setMissingHandler(routeContext, [](const QHttpServerRequest&, QHttpServerResponder& responder) {
QHttpServerResponse resp(QJsonObject{{QStringLiteral("error"), QStringLiteral("not_found")}},

View File

@@ -1,6 +1,8 @@
#include "api/ipc_dbus/CommandDispatcher.h"
#include "message_bus/pipeline/DynamicRuleRegistry.h"
#include "message_bus/pipeline/rules/DynamicRuleRegistry.h"
#include "message_bus/pipeline/discovery/DiscoveryPool.h"
#include "message_bus/pipeline/mapping/MappingRegistry.h"
namespace softbus::api::ipc
{
@@ -22,6 +24,8 @@ softbus::message_bus::pipeline::RuleScope parseScope(const QJsonObject& params)
DispatchResult CommandDispatcher::dispatch(const QString& action, const QJsonObject& params) const
{
auto& reg = softbus::message_bus::pipeline::DynamicRuleRegistry::instance();
auto& mappingReg = softbus::message_bus::pipeline::MappingRegistry::instance();
auto& discovery = softbus::message_bus::pipeline::DiscoveryPool::instance();
if (action == QStringLiteral("add_filter")) {
softbus::message_bus::pipeline::FilterRule rule;
rule.id = params.value(QStringLiteral("id")).toString();
@@ -57,7 +61,45 @@ DispatchResult CommandDispatcher::dispatch(const QString& action, const QJsonObj
QJsonObject{{QStringLiteral("id"), id}}};
}
if (action == QStringLiteral("list_rules") || action == QStringLiteral("get_status")) {
return {0, QStringLiteral("ok"), reg.status()};
QJsonObject status = reg.status();
status.insert(QStringLiteral("discovery"), discovery.status());
status.insert(QStringLiteral("mapping"), mappingReg.status());
return {0, QStringLiteral("ok"), status};
}
if (action == QStringLiteral("add_mapping_rule")) {
softbus::message_bus::pipeline::MappingRule rule;
const QString keyText = params.value(QStringLiteral("compositeKey")).toString();
rule.compositeKey = keyText.toULongLong();
rule.domPath = params.value(QStringLiteral("domPath")).toString();
rule.scale = params.value(QStringLiteral("scale")).toDouble(1.0);
rule.offset = params.value(QStringLiteral("offset")).toDouble(0.0);
rule.unit = params.value(QStringLiteral("unit")).toString();
if (!mappingReg.upsert(rule)) {
return {4004, QStringLiteral("invalid_mapping_rule"), {}};
}
return {0, QStringLiteral("ok"), QJsonObject{{QStringLiteral("compositeKey"), keyText}}};
}
if (action == QStringLiteral("remove_mapping_rule")) {
const QString keyText = params.value(QStringLiteral("compositeKey")).toString();
const bool ok = mappingReg.remove(keyText.toULongLong());
return {ok ? 0 : 4043, ok ? QStringLiteral("ok") : QStringLiteral("mapping_rule_not_found"),
QJsonObject{{QStringLiteral("compositeKey"), keyText}}};
}
if (action == QStringLiteral("list_mapping_rules")) {
return {0, QStringLiteral("ok"), QJsonObject{{QStringLiteral("items"), mappingReg.list()}}};
}
if (action == QStringLiteral("discovery_start")) {
const auto ttlMs = params.value(QStringLiteral("ttlMs")).toInteger(0);
discovery.startSniffing(ttlMs);
return {0, QStringLiteral("ok"), discovery.status()};
}
if (action == QStringLiteral("discovery_stop")) {
discovery.stopSniffing();
return {0, QStringLiteral("ok"), discovery.status()};
}
if (action == QStringLiteral("discovery_clear")) {
discovery.clear();
return {0, QStringLiteral("ok"), discovery.status()};
}
return {4040, QStringLiteral("unknown_action"), {}};
}