Skip to content

EasyNVR/pluginsdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EasyNVR主机与插件通信建立.md

详细通信流程

阶段 1:初始化与连接

1.1 主机初始化

// 主机核心初始化代码
hostSDK := host.NewHost(50051) // 初始化Host SDK,监听50051端口

// 注册命令处理器:响应插件的请求,或处理自身下发命令的回调
hostSDK.AddRequestHandler("start", func(pluginID, requestID string, args json.RawMessage) (interface{}, error) {
    // 处理start命令逻辑
    return map[string]interface{}{"status": "started", "task": "ymtask"}, nil
})
hostSDK.AddRequestHandler("stop", func(pluginID, requestID string, args json.RawMessage) (interface{}, error) {
    // 处理stop命令逻辑
    return map[string]interface{}{"status": "stopped"}, nil
})

// 启动Host服务(异步执行,避免阻塞)
go func() {
    if err := hostSDK.Start(); err != nil {
        slog.Error("Host failed", "err", err)
    }
}()
  • 主机创建<font style="color:rgb(0, 0, 0);">Host</font>实例并指定监听端口(50051);
  • 注册<font style="color:rgb(0, 0, 0);">start/stop</font>命令的处理器,用于响应插件的主动调用或处理自身下发命令的反馈;
  • 异步启动 Host 服务,等待插件连接。

1.2 插件初始化与连接主机

// 插件核心初始化代码
cb := plugin.PluginCallback{
    OnShutdown: func() {
        slog.Info("plugin shutdown")
    },
}

// 初始化Plugin SDK,指定插件信息和主机地址
sdk, err := plugin.NewPlugin(data.Plugin{
    ID:         "1",
    Name:       "hello-plugin",
    Version:    "1.0.0",
    ServerAddr: "localhost:50051", // 连接主机的地址
}, cb)
if err != nil {
    slog.Error("new plugin error", "error", err)
    return
}
defer sdk.Close()

// 注册响应主机下发命令的处理器
sdk.AddRequestHandler("start", func(requestID string, args json.RawMessage) (interface{}, error) {
    // 处理主机下发的start命令
    return map[string]interface{}{"status": "started", "task": "task"}, nil
})
sdk.AddRequestHandler("stop", func(requestID string, args json.RawMessage) (interface{}, error) {
    // 处理主机下发的stop命令
    return map[string]interface{}{"status": "stopped"}, nil
})

// 注册处理主机响应的处理器(插件主动调用主机后,接收结果用)
sdk.AddResponseHandler("start", func(requestID string, args json.RawMessage) (interface{}, error) {
    // 处理主机对start调用的响应
    return map[string]interface{}{"status": "stopped"}, nil
})
sdk.AddResponseHandler("stop", func(requestID string, args json.RawMessage) (interface{}, error) {
    // 处理主机对stop调用的响应
    return map[string]interface{}{"status": "stopped"}, nil
})
  • 插件创建<font style="color:rgb(0, 0, 0);">Plugin</font>实例,指定自身标识(ID / 名称 / 版本)和主机地址;
  • 注册<font style="color:rgb(0, 0, 0);">RequestHandler</font>:用于接收并响应主机主动下发<font style="color:rgb(0, 0, 0);">start/stop</font>命令;
  • 注册<font style="color:rgb(0, 0, 0);">ResponseHandler</font>:用于接收主机对插件主动调用的响应结果;
  • 插件启动后自动连接主机,连接成功后打印<font style="color:rgb(0, 0, 0);">Plugin started and connected to host</font>

阶段 2:主机主动下发命令(start)

2.1 主机触发下发逻辑

主机启动 10 秒后,遍历已连接的插件,主动下发<font style="color:rgba(0, 0, 0, 0.85) !important;">start</font>命令:

// 主机定时任务(10秒后执行)
time.AfterFunc(10*time.Second, func() {
    plugins := hostSDK.ListPlugins() // 获取已连接的插件列表
    for id := range plugins {
        // 构造start命令的参数
        startRequest := data.StartRequest{}
        startRequest.Task = "back"
        request, _ := json.Marshal(&startRequest)
        
        // 给插件发送start命令
        ch, err := hostSDK.SendCommand(id, "start", request)
        if err != nil {
            slog.Error("Send command failed", "err", err)
            continue
        }
        // 异步接收插件的响应结果
        go func() {
            resp := <-ch
            if resp.Success {
                slog.Info("Command succeeded", "plugin", id, "data", string(resp.Data))
            } else {
                slog.Error("Command failed", "plugin", id, "error", resp.Error)
            }
        }()
    }
})

2.2 插件接收并响应 start 命令

插件的<font style="color:rgba(0, 0, 0, 0.85) !important;">start</font>命令处理器被触发,处理请求并返回结果:

// 插件的start命令处理器
sdk.AddRequestHandler("start", func(requestID string, args json.RawMessage) (interface{}, error) {
    slog.Info("Received 'start' from host", "request_id", requestID, "args", args)
    // 返回响应结果给主机
    return map[string]interface{}{
        "status": "started",
        "task":   "task",
    }, nil
})
  • 插件打印日志:<font style="color:rgb(0, 0, 0);">Received 'start' from host request_id=xxx args="{\"task\":\"back\"}"</font>
  • 插件将结果序列化后返回给主机,主机接收后打印<font style="color:rgb(0, 0, 0);">Command succeeded</font>日志。

阶段 3:插件主动调用主机(stop)

3.1 插件触发调用逻辑

插件启动 10 秒后,主动调用主机的<font style="color:rgba(0, 0, 0, 0.85) !important;">stop</font>命令:

// 插件定时任务(10秒后执行)
time.AfterFunc(10*time.Second, func() {
    go func() {
        rawData := json.RawMessage(`{"table": "users"}`) // 构造调用参数
        // 调用主机的stop命令
        result, err := sdk.CallHost("stop", rawData)
        if err != nil {
            slog.Error("CallHost failed", "err", err)
        } else {
            slog.Info("Host returned", "result", result)
        }
    }()
})

3.2 主机接收并响应 stop 命令

主机的<font style="color:rgba(0, 0, 0, 0.85) !important;">stop</font>命令处理器被触发,处理插件的调用请求并返回结果:

// 主机的stop命令处理器
hostSDK.AddRequestHandler("stop", func(pluginID, requestID string, args json.RawMessage) (interface{}, error) {
    slog.Info("Host handling 'stop'", "plugin", pluginID, "request_id", requestID)
    return map[string]interface{}{"status": "stopped"}, nil
})

3.3 插件接收主机的响应结果

插件的<font style="color:rgba(0, 0, 0, 0.85) !important;">stop</font>响应处理器被触发,接收主机返回的结果:

// 插件的stop响应处理器
sdk.AddResponseHandler("stop", func(requestID string, args json.RawMessage) (interface{}, error) {
    slog.Info("Received 'stop' from host", "request_id", requestID)
    return map[string]interface{}{"status": "stopped"}, nil
})
  • 插件打印日志:<font style="color:rgb(0, 0, 0);">Received 'stop' from host request_id=xxx</font>
  • 插件打印主机返回结果:<font style="color:rgb(0, 0, 0);">Host returned result="{\"status\":\"stopped\"}"</font>

阶段 4:终止流程

主机监听系统退出信号(SIGINT/SIGTERM),收到信号后停止服务:

// 主机终止逻辑
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
hostSDK.Stop() // 停止Host服务,断开与插件的连接

插件通过<font style="color:rgba(0, 0, 0, 0.85) !important;">defer sdk.Close()</font>关闭连接。

示例运行结果演示

主机端输出

插件端输出

关键 API 说明

5.1 主机侧核心 API

API 作用
<font style="color:rgb(0, 0, 0);">host.NewHost(port int)</font> 初始化 Host 实例,指定监听端口
<font style="color:rgb(0, 0, 0);">hostSDK.AddRequestHandler(cmd string, handler)</font> 注册命令处理器,响应插件的主动调用
<font style="color:rgb(0, 0, 0);">hostSDK.Start()</font> 启动 Host 服务,监听端口等待插件连接
<font style="color:rgb(0, 0, 0);">hostSDK.ListPlugins()</font> 获取已连接的插件列表
<font style="color:rgb(0, 0, 0);">hostSDK.SendCommand(pluginID, cmd, args)</font> 给指定插件下发命令,返回通道接收插件响应
<font style="color:rgb(0, 0, 0);">hostSDK.Stop()</font> 停止 Host 服务

5.2 插件侧核心 API

API 作用
<font style="color:rgb(0, 0, 0);">plugin.NewPlugin(pluginInfo, callback)</font> 初始化 Plugin 实例,指定插件信息和主机地址,设置回调函数
<font style="color:rgb(0, 0, 0);">sdk.AddRequestHandler(cmd string, handler)</font> 注册命令处理器,响应主机下发的命令
<font style="color:rgb(0, 0, 0);">sdk.AddResponseHandler(cmd string, handler)</font> 注册响应处理器,接收主机对插件主动调用的响应结果
<font style="color:rgb(0, 0, 0);">sdk.CallHost(cmd string, args)</font> 主动调用主机的指定命令,返回主机的响应结果
<font style="color:rgb(0, 0, 0);">sdk.Close()</font> 关闭插件与主机的连接

About

插件端

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages