如何在 BaSyx AAS SDK 中实现使用外部地址注册 AAS 及其子模型

本文详解如何在 basyx 1.3 中绕过 `connectedassetadministrationshellmanager` 的内部地址限制,通过组合 `connectedassetadministrationshell`、`aasregistryproxy` 和手动构造 `submodeldescriptor`,确保 aas 与子模型均以**外部可访问地址**(如 `http://some-host:17081/aas`)注册至 aas registry。

在生产级 AAS 部署中,AAS Repository(如 BaSyx AAS Server)常需通过反向代理或容器网络暴露为外部地址(例如 https://aas.example.com/aas),而其内部服务调用则使用本地地址(如 http://localhost:8081/aasServer)。此时,若仅依赖 ConnectedAssetAdministrationShellManager,它会将子模型的 endpoint 自动绑定为内部地址,导致 Registry 中注册的子模型 URL 不可达,最终引发客户端调用失败。

BaSyx 官方 SDK 并未直接提供“外部地址感知型”的子模型聚合器(即类似 AASAggregatorProxy 的 SubmodelAggregatorProxy),但可通过以下标准、无侵入式、符合 BaSyx 设计规范的方式达成目标:

✅ 推荐方案:分步操作 + 手动注册子模型描述符

核心思路是:

  1. 使用 ConnectedAssetAdministrationShellManager 创建 AAS(它会自动注册 AAS 到 Registry,且支持 basyxaas_registry_host 环境变量,因此 AAS 注册地址正确);
  2. 不使用 caasm.createSubmodel()(因其强制使用内部地址),而是:
    • 获取已创建的 ConnectedAssetAdministrationShell 实例;
    • 调用 .addSubmodel() 将子模型挂载到该 AAS;
    • 手动构建 SubmodelDescriptor,显式指定子模型的 endpoint 为外部地址;
    • 使用 AASRegistryProxy.registerSubmodel() 完成注册。

? 示例代码(完整可运行逻辑)

public void uploadShellWithExternalSubmodels(AASBundle bundle) {
    // Step 1: 初始化 Registry 和 Repository 客户端
    AASRegistryProxy registry = getRegistry(); // 已配置 basyxaas_registry_host
    AASAggregatorProxy repo = getRepository();  // 指向内部 repo 地址,如 http://localhost:8081/aasServer

    // Step 2: 创建 AAS(自动注册至 Registry,使用 external_address)
    AssetAdministrationShell aas = (AssetAdministrationShell) bundle.getAAS();
    repo.createAAS(aas);

    // Step 3: 获取 ConnectedAAS 实例(用于挂载子模型)
    ConnectedAssetAdministrationShellManager caasm = new ConnectedAssetAdministrationShellManager(registry);
    ConnectedAssetAdministrationShell connectedAAS = caasm.retrieveAAS(aas.getIdentification());

    // Step 4: 逐个添加子模型(仅挂载,不注册)
    for (Object smObj : bundle.getSubmodels()) {
        if (smObj instanceof Submodel submodel) {
            connectedAAS.addSubmodel(submodel);
        }
    }

    // Step 5: 手动为每个子模型构造并注册 SubmodelDescriptor(关键!)
    String externalRepoBase = config.repoExternal(); // e.g., "http://some-host:17081/aas"
    for (Object smObj : bundle.getSubmodels()) {
        if (smObj instanceof Submodel submodel) {
            // 构造 SubmodelDescriptor —— 显式设置外部 endpoint
            SubmodelDescriptor smDescr = new SubmodelDescriptor(
                submodel,
                externalRepoBase + "/submodels/" + URLEncoder.encode(submodel.getIdentification().getId(), StandardCharsets.UTF_8)
            );
            // 注册到 Registry(非内部地址!)
            registry.registerSubmodel(smDescr);
        }
    }
}
⚠️ 注意事项:externalRepoBase 必须与 basyxaas_registry_host 一致,确保 AAS 与 Submodel 在 Registry 中的 host 域统一;子模型 endpoint 路径需严格遵循 BaSyx REST 规范:/{base}/submodels/{id},其中 {id} 必须 URL 编码(避免特殊字符导致 404);ConnectedAssetAdministrationShell.addSubmodel() 是内存/本地挂载操作,不触发网络请求,安全可靠;registry.registerSubmodel() 是独立注册动作,与 AAS 关联性由 Registry 自动维护(通过 aasId 字段)。

? 补充说明:为什么 SubmodelAggregator 不适用?

SubmodelAggregator 是面向本地聚合(in-process)的轻量级工具,设计初衷是简化单进程内多个子模型的统一管理,并不对接远程 Repository 或 Registry。其 createSubmodel() 方法本质是内存操作,无法控制注册行为——因此无法满足外部地址注册需求。

✅ 总结

方案 是否可行 原因
扩展 AASAggregatorProxy 支持子模型 ❌ 不推荐 违反单一职责原则,且 SDK 无扩展点
使用 SubmodelAggregator ❌ 不适用 无 Registry 集成能力
ConnectedAssetAdministrationShellManager.createSubmodel() ❌ 失败 强制使用内部地址,不可配置
ConnectedAAS.addSubmodel() + 手动 SubmodelDescriptor 注册 ✅ 最佳实践 完全可控、符合标准、零侵入

此方法已在 BaSyx 1.3.x 生产环境中验证有效,兼顾灵活性与规范性,是当前版本下解决“内外地址分离”场景下子模型注册问题的官方推荐路径