验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

Go语言otns源码分析

阅读:348 来源:乙速云 作者:代码code

Go语言otns源码分析

      proto文件

      这个例子中只有一个proto文件,位于ot-ns-main/visualize/grpc/pb下,里面的service也只定义了两个rpc方法:

      service VisualizeGrpcService {
          //    rpc Echo (EchoRequest) returns (EchoResponse);
          rpc Visualize (VisualizeRequest) returns (stream VisualizeEvent);
          rpc Command (CommandRequest) returns (CommandResponse);
      }
      • Visualize (VisualizeRequest) returns (stream VisualizeEvent)

      这个方法接受一个VisualizeRequest,返回VisualizeEvent流。两个消息定义如下:

      message VisualizeRequest {
      }
      message VisualizeEvent {
          oneof type {
              AddNodeEvent add_node = 1;
              DeleteNodeEvent delete_node = 2;
              SetNodeRloc16Event set_node_rloc16 = 3;
              SetNodeRoleEvent set_node_role = 4;
              SetNodePosEvent set_node_pos = 5;
              SetNodePartitionIdEvent set_node_partition_id = 6;
              OnNodeFailEvent on_node_fail = 7;
              OnNodeRecoverEvent on_node_recover = 8;
              SetParentEvent set_parent = 9;
              CountDownEvent count_down = 10;
              ShowDemoLegendEvent show_demo_legend = 11;
              AdvanceTimeEvent advance_time = 12;
              AddRouterTableEvent add_router_table = 13;
              RemoveRouterTableEvent remove_router_table = 14;
              AddChildTableEvent add_child_table = 15;
              RemoveChildTableEvent remove_child_table = 16;
              SendEvent send = 17;
              SetSpeedEvent set_speed = 18;
              HeartbeatEvent heartbeat = 19;
              OnExtAddrChangeEvent on_ext_addr_change = 20;
              SetTitleEvent set_title = 21;
              SetNodeModeEvent set_node_mode = 22;
              SetNetworkInfoEvent set_network_info = 23;
          }
      }

      请求为空,而VisualizeEvent里面使用oneof关键字包含了很多的消息体,每个消息体封装了一个事件。

      • Command (CommandRequest) returns (CommandResponse)

      这个方法接受CommandRequest并返回CommandResponse,两个消息体定义如下:

      message CommandRequest {
          string command = 1;
      }
      message CommandResponse {
          repeated string output = 1;
      }

      CommandResponse中的output在go中会声明为string[]

      visualize/grpc/replay目录下的文件

      • grpcField(未包含pb)

      定义了一个结构grpcField,里面包含了节点信息、当前时间与速度、标题信息、网络信息、及其设置。

      type grpcField struct {
         nodes       map[NodeId]*grpcNode
         curTime     uint64
         curSpeed    float64
         speed       float64
         titleInfo   visualize.TitleInfo
         networkInfo visualize.NetworkInfo
      }
      • grpcNode(未包含pb)

      定义了节点结构grpcNode,包含各种信息,还有一个new这个结构的函数

      type grpcNode struct {
         nodeid      NodeId
         extaddr     uint64
         x           int
         y           int
         radioRange  int
         mode        NodeMode
         rloc16      uint16
         role        OtDeviceRole
         partitionId uint32
         failed      bool
         parent      uint64
         routerTable map[uint64]struct{}
         childTable  map[uint64]struct{}
      }
      • grpcServer(包含pb)

      自定义了一个grpcServer,包含信息如下

      type grpcServer struct {
         vis                *grpcVisualizer
         server             *grpc.Server
         address            string
         visualizingStreams map[*grpcStream]struct{}
      }

      同时按照接口要求实现了Visualize()Command()方法,还自定义了其他的方法如runstopprepareStream等等,看名字就容易知道是什么用途

      • grpcStream(包含pb)

      里面自定义了一个结构grpcStream,使用这个文件中的newGrpcStream可以将Visualize函数的服务端流赋到这个结构中

      • grpcVisualizer(包含pb)

      其中自定义了一个结构:

          type grpcVisualizer struct {
             simctrl             visualize.SimulationController
             server              *grpcServer
             f                   *grpcField
             showDemoLegendEvent *pb.VisualizeEvent
             replay              *replay.Replay
             sync.Mutex
          }
      • 需要注意的是这个结构继承了互斥锁sync.Mutex,并且包含了上面的grpcServer、grpcServer结构,这个文件里面的函数大概都是添加、删除节点或者修改什么信息之类的,基本是调用了grpcFieldgrpcServer文件里面的函数,但是在调用之前加了锁。

      • 这个结构实现了visualize/types.go中的Visualizer接口

      • 并且,这个结构中包含了visualize.SimulationController接口的字段,而visualize.SimulationController定义如下:

      type SimulationController interface {
          Command(cmd string) ([]string, error)
      }

      大概就是命令的入口。

      cmd/otns-replay目录下的文件

      grpc_Service(包含pb)
      • 定义了grpcService结构,并且实现了VisualizeCommand两个方法

      type grpcService struct {
         replayFile string
      }

      2. grpcService结构下的visualizeStream()函数

      grpcService的replay文件检验并打开,并且逐行读取内容,并解析到var entry pb.ReplayEntry中,再通过stream将entry.Event发送到服务的客户端

      • 实现的Visualize方法:

      启动visualizeStream()协程,创建一个心跳事件,每隔一秒心跳一下,直到上面的visualizeStream()读取完成

      otns_replay(包含pb)

      main()函数

      一系列的校验和配置参数之后,用上面的grpcService结构注册服务端,在本机地址8999端口监听。然后就是配置和打开网页

      cmd/otns/otns.go文件

      调用了otns_main/otns_main.go下的Main()函数:

      首先依然是解析和配置参数和环境:

      parseArgs()
      simplelogger.SetLevel(simplelogger.ParseLevel(args.LogLevel))
      parseListenAddr()
      rand.Seed(time.Now().UnixNano())
      // run console in the main goroutine
      ctx.Defer(func() {
         _ = os.Stdin.Close()
      })
      handleSignals(ctx)

      然后是打开replay文件并创建visualizer实例:

      var vis visualize.Visualizer
      if visualizerCreator != nil {
         vis = visualizerCreator(ctx, &args)
      }
      visGrpcServerAddr := fmt.Sprintf("%s:%d", args.DispatcherHost, args.DispatcherPort-1)
      replayFn := ""
      if !args.NoReplay {
         replayFn = fmt.Sprintf("otns_%s.replay", os.Getenv("PORT_OFFSET"))
      }
      if vis != nil {
         vis = visualizeMulti.NewMultiVisualizer(
            vis,
            visualizeGrpc.NewGrpcVisualizer(visGrpcServerAddr, replayFn),
         )
      } else {
         vis = visualizeGrpc.NewGrpcVisualizer(visGrpcServerAddr, replayFn)
      }

      创建一个新模拟,并设置CmdRunnerVisualizer

      sim := createSimulation(ctx)
      rt := cli.NewCmdRunner(ctx, sim)
      sim.SetVisualizer(vis)

      启动一个协程运行模拟:

      go sim.Run()

      启动客户命令行协程:

      go func() {
         err := cli.Run(rt, cliOptions)
         ctx.Cancel(errors.Wrapf(err, "console exit"))
      }()

      设置并打开网页:

      go func() {
         siteAddr := fmt.Sprintf("%s:%d", args.DispatcherHost, args.DispatcherPort-3)
         err := webSite.Serve(siteAddr)
         if err != nil {
            simplelogger.Errorf("site quited: %+v, OTNS-Web won't be available!", err)
         }
      }()
      if args.AutoGo {
         go autoGo(ctx, sim)
      }
      web.ConfigWeb(args.DispatcherHost, args.DispatcherPort-2, args.DispatcherPort-1, args.DispatcherPort-3)
      simplelogger.Debugf("open web: %v", args.OpenWeb)
      if args.OpenWeb {
         _ = web.OpenWeb(ctx)
      }

      Visualizer启动:

      vis.Run() // visualize must run in the main thread

      simulation目录下的文件

      simulationgrpcVisualizercmdRunner通信的桥梁。

      type.go

      定义了CmdRunner接口:

      type CmdRunner interface {
         RunCommand(cmd string, output io.Writer) error
      }
      simulationController.go
      • 定义了simulationController类,这个类实现了visualize.SimulationController接口,也就是grpcVisualizer里有的字段:

      type simulationController struct {
         sim *Simulation
      }
      func (sc *simulationController) Command(cmd string) ([]string, error) {
         var outputBuilder strings.Builder
         sim := sc.sim
         err := sim.cmdRunner.RunCommand(cmd, &outputBuilder)
         if err != nil {
            return nil, err
         }
         output := strings.Split(outputBuilder.String(), "n")
         if output[len(output)-1] == "" {
            output = output[:len(output)-1]
         }
         return output, nil
      }
      • 还定义了同样实现了visualize.SimulationController接口的只读类,这里不展开说了。

      • 还有一个NewSimulationController(sim *Simulation)函数产生simulationController

      • simulationController应该是一个介于Command和Simulation之间的中介,接收Command并操作CmdRunner更改Simulation,并且输出信息。

      simulation_config.go

      定义了配置和默认配置

      simulation.go
      • simulation结构定义:

      type Simulation struct {
         ctx         *progctx.ProgCtx
         cfg         *Config
         nodes       map[NodeId]*Node
         d           *dispatcher.Dispatcher
         vis         visualize.Visualizer
         cmdRunner   CmdRunner
         rawMode     bool
         networkInfo visualize.NetworkInfo
      }
      • 有一个new产生simulation结构的函数

      • 各种增删改查操作,都是通过simulation结构中的visualize.Visualizer接口函数实现的

      cli目录

      cli目录下定义了CmdRunner及各种指令结构

      ast.go

      定义了各种命令结构

      CmdRunner.go
      • 定义了CmdRunner结构:

      type CmdRunner struct {
         sim           *simulation.Simulation
         ctx           *progctx.ProgCtx
         contextNodeId NodeId
      }
      • 实现simulation/CmdRunner接口的RunCommand方法:

      func (rt *CmdRunner) RunCommand(cmdline string, output io.Writer) error {
         // run the OTNS-CLI command without node contexts
         cmd := Command{}
         if err := ParseBytes([]byte(cmdline), &cmd); err != nil {
            if _, err := fmt.Fprintf(output, "Error: %vn", err); err != nil {
               return err
            }
         } else {
            rt.execute(&cmd, output)
         }
         return nil
      }
      • RunCommand方法中解析配置好命令后,有各种execute...()函数来执行相应的命令,而在这些函数中又是通过调用simulation.Simulation中对应的增删改查函数来实现操作的

    分享到:
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
    相关文章
    {{ v.title }}
    {{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
    你可能感兴趣
    推荐阅读 更多>