OpenRobot.Edge.AppSdk
0.1.0
dotnet add package OpenRobot.Edge.AppSdk --version 0.1.0
NuGet\Install-Package OpenRobot.Edge.AppSdk -Version 0.1.0
<PackageReference Include="OpenRobot.Edge.AppSdk" Version="0.1.0" />
<PackageVersion Include="OpenRobot.Edge.AppSdk" Version="0.1.0" />
<PackageReference Include="OpenRobot.Edge.AppSdk" />
paket add OpenRobot.Edge.AppSdk --version 0.1.0
#r "nuget: OpenRobot.Edge.AppSdk, 0.1.0"
#:package OpenRobot.Edge.AppSdk@0.1.0
#addin nuget:?package=OpenRobot.Edge.AppSdk&version=0.1.0
#tool nuget:?package=OpenRobot.Edge.AppSdk&version=0.1.0
OpenRobot.Edge.AppSdk
边缘应用接入 SDK:连接 EdgeWebApi、自动注册、配置声明与热更新、状态上报、业务数据写入。
应用只写业务逻辑,配置界面、数据存储、状态展示、控制面板交给平台。
快速开始
var builder = ModuleHostBuilder.Create(args);
builder.Services.AddLogging();
builder.Services.AddEdgeApp(o =>
{
o.AppId = "EdgeCDS"; // = IPC ClientId = 平台侧身份,只配这一处
o.DisplayName = "边缘数据服务";
o.Version = "26.10.09.1";
o.Ipc.Transport = IpcTransportKind.NamedPipe;
o.Variables.Add(new AppVariableDeclarationEntry
{
Key = "apps.EdgeCDS.collect.interval",
Title = "采集周期",
Type = AppVariableTypes.Int,
Default = 1000,
Min = 100,
Max = 60000,
Editable = true,
});
o.Collections.Add(new AppCollectionDeclaration
{
Name = "alarms",
Mode = AppCollectionModes.Log,
TtlSeconds = 30 * 24 * 3600,
Indexed = ["$.level"],
});
});
// 让宿主替我们管连接生命周期:PostInitialize 连、Shutdown 断
builder.AddModule<EdgeAppModule>();
await using var host = builder.Build();
await host.RunAsync();
AddEdgeApp 只注册、不连接,与 Communication 库同一约定。连接由宿主发起,两种接法去下面挑一种。
选择宿主接法
一、Modularity(推荐)
[DependsOn(typeof(EdgeAppModule))] // 声明依赖,保证拓扑排序里排在 SDK 之后
public sealed class EdgeCdsModule : StartupModule
{
private readonly EdgeAppClient _edge;
public EdgeCdsModule(EdgeAppClient edge) => _edge = edge;
public override void PostInitialize()
{
// 注册回调要在连接之前 —— 重连后平台可能立刻补发,晚注册就漏了第一条
_edge.ConfigChanged = OnConfigChanged;
}
private Task OnConfigChanged(AppConfigChangedPayload payload)
{
// SDK 已经在回调之前把新值刷进缓存了,这里直接读就是新值。
// payload.ChangedKeys 告诉你是哪些键变了(null = 整份重载),可用来做增量应用。
var interval = _edge.Config.Get<int>("apps.EdgeCDS.collect.interval");
RestartCollector(interval);
return Task.CompletedTask;
}
}
EdgeAppModule 在 PostInitialize 发起连接、在 WaitReadyAsync 最多等 5 秒。
连不上不算启动失败:超时只记一条 Warning,应用按离线模式继续跑,后台会持续重连。
二、Generic Host
services.AddEdgeApp(...);
public sealed class EdgeAppHostedService : IHostedService
{
private readonly EdgeAppRunner _runner;
public EdgeAppHostedService(EdgeAppRunner runner) => _runner = runner;
public Task StartAsync(CancellationToken ct) => _runner.StartAsync(ct);
public Task StopAsync(CancellationToken ct) => _runner.StopAsync();
}
需要知道「到底连上没有」时用 _runner.WaitForConnectedAsync(TimeSpan);它超时返回 false 而不是抛异常。
业务代码
注入 EdgeAppClient:
await edge.ReportStateAsync(new { running = true, throughput = 128 });
await edge.UpsertAsync("devices", "dev-01", device);
await edge.AppendAsync("alarms", new { level = "error", code = "E1024" });
// 注册控制点(在 PostInitialize 里做,别等指令到了再注册)
edge.OnControl("restartCollector", async (cmd, ct) =>
{
if (_collector.IsBusy) return new AppControlResultPayload { Ok = false, Message = "正在采集中,稍后再试" };
await _collector.RestartAsync(ct);
return new AppControlResultPayload { Ok = true, Message = "已重启" };
});
控制点要先在 EdgeAppOptions.Controls 里声明(界面才有按钮),
再在运行时用 OnControl 挂执行器 —— 声明说「有什么按钮」,执行器说「按下去做什么」。
执行器自己判断能不能做:声明不是许可。
| 方法 | 用途 | 失败行为 |
|---|---|---|
ReportStateAsync(state, controls) |
状态上报,建议 2–5 秒一次 | 只记日志 |
UpsertAsync(collection, key, payload) |
按唯一键写入 | 只记日志 |
AppendAsync(collection, payload) |
追加一条(键由平台生成) | 只记日志 |
WriteAsync(collection, records) |
批量写入 | 只记日志 |
GetConfigAsync() |
拉一次生效配置并刷新 Config |
返回 null,保留旧缓存 |
RegisterAsync() / DeclareVariablesAsync() |
手动重发声明 | 抛给调用方 |
读配置是同步的,走本地缓存:
int interval = edge.Config.Get<int>("apps.EdgeCDS.collect.interval");
if (edge.Config.TryGet<string>("apps.EdgeCDS.mode", out var mode)) { ... }
缓存由 SDK 自动维护:连上时拉一次,收到 app.config.changed 时先刷新再回调。
读不到(键不存在 / 类型对不上)返回 default 而不是抛 —— 配置读取散落在业务各处,
为一个漏写的键打断采集循环不值得。
失败不阻断业务:这条链路是管理面旁路,发送失败只记日志。唯一的硬失败是构造期
AppId 为空、以及往未声明的集合里写数据 —— 这两件事写下去只会变成孤儿数据,早抛比晚查好。
协议
topic 常量见 EdgeAppProtocol。前缀固定 app.,点分、无版本前缀、不带应用名 ——
IPC 信封的 target 负责寻址,路由器按 topic 精确匹配且没有通配符。
| 方向 | topic | 载荷类型 |
|---|---|---|
| 应用 → 平台 | app.register |
AppRegisterPayload |
| 应用 → 平台 | app.vars.declare |
AppVariableDeclarationPayload |
| 应用 → 平台 | app.state |
AppStatePayload |
| 应用 → 平台 | app.data |
AppDataPayload |
| 应用 → 平台(请求-响应) | app.config.get |
AppConfigRequestPayload → AppConfigSnapshotPayload |
| 平台 → 应用 | app.config.changed |
AppConfigChangedPayload |
| 平台 → 应用(请求-响应) | app.control.invoke |
AppControlInvokePayload → AppControlResultPayload |
注册与声明在每次连上时自动重发(AutoRegisterOnConnect,默认开),应用不必自己订阅连接事件。
声明内容会算 SHA-256 摘要,平台据此判断「没变就不重复落盘」,所以重复发送是安全的。
当前范围
已实现:注册、变量声明、状态上报、业务数据写入(IPC)、配置变更订阅、
配置读取(app.config.get + 本地缓存)、控制点声明与执行(app.control.invoke)。
后续:配置写入(SetAsync / ApplyAsync,需平台侧校验与审计)、树形 schema(object / array)、
HTTP 数据通道、本地开发模式。
平台侧已接入
app.register(应用台账:注册信息落库 + 在线 / pm2 进程状态) 与app.config.get;控制点的平台侧(下发入口、界面按钮)尚未接入 —— 协议与应用侧都就绪,RequestToAsync一接就能用。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- OpenRobot.Framework.Communication (>= 1.2.3)
- OpenRobot.Framework.Modularity (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 37 | 10/29/2026 |