diff --git a/cli/CmdRunner.go b/cli/CmdRunner.go index 77668279..78984ea5 100644 --- a/cli/CmdRunner.go +++ b/cli/CmdRunner.go @@ -1147,9 +1147,6 @@ func (rt *CmdRunner) executeNetInfo(cc *CommandContext, cmd *NetInfoCmd) { if cmd.Commit != nil { netinfo.Commit = *cmd.Commit } - if cmd.Real != nil { - netinfo.Real = cmd.Real.Yes != nil - } sim.SetNetworkInfo(netinfo) }) } diff --git a/cli/README.md b/cli/README.md index c60ca7cd..493caad8 100644 --- a/cli/README.md +++ b/cli/README.md @@ -498,26 +498,21 @@ Done Set default network and nodes info. ```shell -netinfo [version ""] [commit ""] [real y|n] +netinfo [version ""] [commit ""] ``` -Sets information about OpenThread version and commit used for simulation nodes, as well as whether nodes are real -or simulated. This default information is then shown in the GUI, whenever a node is not selected. When a node is +Sets information about OpenThread version and commit used for simulation nodes. This default information is then +shown in the GUI, whenever a node is not selected. When a node is selected, the node-specific version/commit information will be used instead. In the GUI, when the version/commit message is clicked, a web browser tab will be opened with the Github code for -the particular version/commit. - -NOTE: setting `real` enabled (y) will disable some of the GUI controls of the simulation, such as speed/pause. The -`real` setting remains as set by this command and is not impacted by selecting nodes in the GUI. +the particular OpenThread version/commit. ```bash > netinfo version "Latest" Done > netinfo version "Latest" commit "a1816c1" Done -> netinfo real y -Done > netinfo version "please select a node and then click this text to see the node's code." commit "" Done ``` diff --git a/cli/ast.go b/cli/ast.go index aaafbf30..a5629ff7 100644 --- a/cli/ast.go +++ b/cli/ast.go @@ -178,10 +178,9 @@ type PingCmd struct { // noinspection GoVetStructTag type NetInfoCmd struct { - Cmd struct{} `"netinfo" (` //nolint - Version *string ` "version" @String` //nolint - Commit *string `| "commit" @String` //nolint - Real *YesOrNoFlag `| "real" @@ )+` //nolint + Cmd struct{} `"netinfo" (` //nolint + Version *string ` "version" @String` //nolint + Commit *string `| "commit" @String )+` //nolint } // noinspection GoVetStructTag diff --git a/dispatcher/dispatcher.go b/dispatcher/dispatcher.go index 80cbb222..3d658613 100644 --- a/dispatcher/dispatcher.go +++ b/dispatcher/dispatcher.go @@ -128,7 +128,7 @@ type Dispatcher struct { } func NewDispatcher(ctx *progctx.ProgCtx, cfg *Config, cbHandler CallbackHandler) *Dispatcher { - logger.AssertTrue(!cfg.Real || cfg.Speed == 1) + logger.AssertTrue(!cfg.Realtime || cfg.Speed == 1) var err error ln, unixSocketFile := newUnixSocket(cfg.SimulationId) vis := visualize.NewNopVisualizer() @@ -497,13 +497,7 @@ func (d *Dispatcher) processNextEvent(simSpeed float64) bool { } time.Sleep(sleepTime) - if d.cfg.Real { - curTime := d.speedStartTime + uint64(float64(time.Since(d.speedStartRealTime)/time.Microsecond)*simSpeed) - if curTime > d.pauseTime { - curTime = d.pauseTime - } - d.advanceTime(curTime) - } else if time.Since(d.lastVizTime) >= d.cfg.VizUpdateTime { + if time.Since(d.lastVizTime) >= d.cfg.VizUpdateTime { curTime := d.speedStartTime + uint64(float64(time.Since(d.speedStartRealTime)/time.Microsecond)*simSpeed) if curTime > d.pauseTime { curTime = d.pauseTime @@ -659,11 +653,6 @@ func (d *Dispatcher) eventsReader() { func (d *Dispatcher) advanceNodeTime(node *Node, timestamp uint64, force bool) { logger.AssertNotNil(node) - if d.cfg.Real { - node.CurTime = timestamp - return - } - oldTime := node.CurTime if timestamp <= oldTime && !force { // node time was already equal to or newer than the requested timestamp @@ -808,9 +797,7 @@ func (d *Dispatcher) checkRadioReachable(src *Node, dst *Node) bool { d.radioModel.CheckRadioReachable(src.RadioNode, dst.RadioNode) } -func (d *Dispatcher) sendOneRadioFrame(evt *Event, - srcnode *Node, dstnode *Node) { - logger.AssertFalse(d.cfg.Real) +func (d *Dispatcher) sendOneRadioFrame(evt *Event, srcnode *Node, dstnode *Node) { logger.AssertTrue(EventTypeRadioCommStart == evt.Type || EventTypeRadioRxDone == evt.Type) logger.AssertTrue(srcnode != dstnode) @@ -843,7 +830,6 @@ func (d *Dispatcher) sendOneRadioFrame(evt *Event, } func (d *Dispatcher) setAlive(nodeid NodeId) { - logger.AssertFalse(d.cfg.Real) logger.AssertFalse(d.isDeleted(nodeid)) d.aliveNodes[nodeid] = struct{}{} } @@ -863,7 +849,6 @@ func (d *Dispatcher) isDeleted(nodeid NodeId) bool { } func (d *Dispatcher) setSleeping(nodeid NodeId) { - logger.AssertFalse(d.cfg.Real) logger.AssertFalse(d.isDeleted(nodeid)) delete(d.aliveNodes, nodeid) } @@ -927,7 +912,8 @@ func (d *Dispatcher) handleStatusPush(srcnode *Node, data string) { continue } if sp[0] == "transmit" { - d.visStatusPushTransmit(srcnode, sp[1]) + // 'transmit' status is currently not visualized: This is already done by OTNS based on + // radio frames transmitted. } else if sp[0] == "role" { role, err := strconv.Atoi(sp[1]) logger.PanicIfError(err) @@ -1004,7 +990,7 @@ func (d *Dispatcher) handleStatusPush(srcnode *Node, data string) { mode := ParseNodeMode(sp[1]) d.vis.SetNodeMode(srcid, mode) } else { - logger.Warnf("received unknown status push: %s=%s", sp[0], sp[1]) + logger.Errorf("received unknown status push: %s=%s", sp[0], sp[1]) } } } @@ -1054,75 +1040,6 @@ func (d *Dispatcher) setNodeRloc16(srcid NodeId, rloc16 uint16) { d.vis.SetNodeRloc16(srcid, rloc16) } -func (d *Dispatcher) visStatusPushTransmit(srcnode *Node, s string) { - var fcf wpan.FrameControl - - // only visualize `transmit` status emitting in real mode because simulation nodes already have radio events visualized - if !d.cfg.Real { - return - } - - parts := strings.Split(s, ",") - - if len(parts) < 3 { - logger.Panicf("invalid status push: transmit=%s", s) - } - - channel, err := strconv.Atoi(parts[0]) - logger.PanicIfError(err) - fcfval, err := strconv.ParseUint(parts[1], 16, 16) - logger.PanicIfError(err) - fcf = wpan.FrameControl(fcfval) - - seq, err := strconv.Atoi(parts[2]) - logger.PanicIfError(err) - - dstAddrMode := fcf.DestAddrMode() - - visInfo := &visualize.MsgVisualizeInfo{ - Channel: uint8(channel), - FrameControl: fcf, - Seq: uint8(seq), - } - - if dstAddrMode == wpan.AddrModeExtended { - dstExtend, err := strconv.ParseUint(parts[3], 16, 64) - logger.PanicIfError(err) - - visInfo.DstAddrExtended = dstExtend - - dstnode := d.extaddrMap[dstExtend] - if dstnode != srcnode && dstnode != nil { - d.visSend(srcnode.Id, dstnode.Id, visInfo) - } else { - d.visSend(srcnode.Id, InvalidNodeId, visInfo) - } - } else if dstAddrMode == wpan.AddrModeShort { - dstShortVal, err := strconv.ParseUint(parts[3], 16, 16) - logger.PanicIfError(err) - - dstShort := uint16(dstShortVal) - visInfo.DstAddrShort = dstShort - - if dstShort != BroadcastRloc16 { - // unicast message should only be dispatched to target node with the rloc16 - dstnodes := d.rloc16Map[dstShort] - - if len(dstnodes) > 0 { - for _, dstnode := range dstnodes { - d.visSend(srcnode.Id, dstnode.Id, visInfo) - } - } else { - d.visSend(srcnode.Id, InvalidNodeId, visInfo) - } - } else { - d.vis.Send(srcnode.Id, BroadcastNodeId, visInfo) - } - } else { - d.vis.Send(srcnode.Id, BroadcastNodeId, visInfo) - } -} - func (d *Dispatcher) visSendFrame(srcid NodeId, dstid NodeId, pktframe *wpan.MacFrame, commData RadioCommEventData) { d.visSend(srcid, dstid, &visualize.MsgVisualizeInfo{ Channel: pktframe.Channel, @@ -1159,9 +1076,6 @@ func (d *Dispatcher) advanceTime(ts uint64) { logger.AssertTrue(d.CurTime <= ts, "%v > %v", d.CurTime, ts) if d.CurTime < ts { d.CurTime = ts - if d.cfg.Real { - d.syncAllNodes() - } } if time.Since(d.lastVizTime) >= d.cfg.VizUpdateTime { diff --git a/dispatcher/dispatcher_config.go b/dispatcher/dispatcher_config.go index 0d3623ab..5152c16a 100644 --- a/dispatcher/dispatcher_config.go +++ b/dispatcher/dispatcher_config.go @@ -35,7 +35,7 @@ import ( type Config struct { Speed float64 - Real bool + Realtime bool DumpPackets bool PcapEnabled bool PcapFrameType pcap.FrameType @@ -49,7 +49,7 @@ type Config struct { func DefaultConfig() *Config { return &Config{ Speed: 1, - Real: false, + Realtime: false, DumpPackets: false, PcapEnabled: true, PcapFrameType: pcap.FrameTypeWpan, diff --git a/otns_main/otns_main.go b/otns_main/otns_main.go index 0d4c287e..4a760e43 100644 --- a/otns_main/otns_main.go +++ b/otns_main/otns_main.go @@ -64,7 +64,7 @@ type MainArgs struct { LogFileLevel string WatchLevel string OpenWeb bool - Real bool + Realtime bool ListenAddr string DispatcherHost string DispatcherPort int @@ -100,7 +100,7 @@ func parseArgs() { flag.StringVar(&args.LogFileLevel, "logfile", "debug", "set OTNS + node file logging level: trace, debug, info, warn, error, off.") flag.StringVar(&args.WatchLevel, "watch", "off", "set default watch (display) level for new nodes: trace, debug, info, note, warn, error, off.") flag.BoolVar(&args.OpenWeb, "web", true, "open web visualization") - flag.BoolVar(&args.Real, "real", false, "use real mode (for real devices - currently NOT SUPPORTED)") + flag.BoolVar(&args.Realtime, "realtime", false, "use real-time mode (forced speed=1 and autogo)") flag.StringVar(&args.ListenAddr, "listen", fmt.Sprintf("localhost:%d", InitialDispatcherPort), "specify UDP listen address and port-base") flag.BoolVar(&args.DumpPackets, "dump-packets", false, "dump packets") flag.StringVar(&args.PcapType, "pcap", pcap.FrameTypeWpanStr, "PCAP file type: 'off', 'wpan', or 'wpan-tap' (name is \"current.pcap\")") @@ -283,7 +283,7 @@ func createSimulation(simId int, ctx *progctx.ProgCtx) (*simulation.Simulation, } simcfg.Speed = speed simcfg.ReadOnly = args.ReadOnly - simcfg.Real = args.Real + simcfg.Realtime = args.Realtime simcfg.DispatcherHost = args.DispatcherHost simcfg.DispatcherPort = args.DispatcherPort simcfg.DumpPackets = args.DumpPackets diff --git a/otnstester/OtnsTest.go b/otnstester/OtnsTest.go index c09726ac..7cc59032 100644 --- a/otnstester/OtnsTest.go +++ b/otnstester/OtnsTest.go @@ -336,7 +336,7 @@ func (ot *OtnsTest) visualizeStreamReadRoutine() { ot.ExpectTrue(err == nil || ot.visualizeStream.Context().Err() != nil) if err == nil { - logger.Warnf("Visualize: %+v", evt) + logger.Infof("Visualize: %+v", evt) ot.pendingVisualizeEvents <- evt } } @@ -367,6 +367,17 @@ func (ot *OtnsTest) ExpectVisualizeAddNode(nodeid NodeId, x int, y int, radioRan }) } +func (ot *OtnsTest) ExpectVisualizeDeleteNode(nodeid NodeId) { + ot.ExpectVisualizeEvent(func(evt *visualize_grpc_pb.VisualizeEvent) bool { + delNode := evt.GetDeleteNode() + if delNode == nil { + return false + } + + return delNode.NodeId == int32(nodeid) + }) +} + func Instance(t *testing.T) *OtnsTest { if otnsTestSingleton == nil { otnsTestSingleton = NewOtnsTest(t) @@ -387,7 +398,7 @@ func NewOtnsTest(t *testing.T) *OtnsTest { T: t, otnsDone: make(chan struct{}), pendingOutput: make(chan string, 1000), - pendingVisualizeEvents: make(chan *visualize_grpc_pb.VisualizeEvent, 1000), + pendingVisualizeEvents: make(chan *visualize_grpc_pb.VisualizeEvent, 100000), } os.Args = append(os.Args, "-log", "debug", "-web=false", "-autogo=false", "-watch", "info") diff --git a/otnstester/tests/otns_basic_test.go b/otnstester/tests/otns_basic_test.go index a6e6ed74..3178dae7 100644 --- a/otnstester/tests/otns_basic_test.go +++ b/otnstester/tests/otns_basic_test.go @@ -97,7 +97,10 @@ func testDelManyNodes(test *otnstester.OtnsTest) { test.Start("testDelManyNodes") for i := 0; i < 32; i++ { - test.AddNode("router", (i%6)*100, (i/6)*150) + x := (i % 6) * 100 + y := (i / 6) * 150 + test.AddNode("router", x, y) + test.ExpectVisualizeAddNode(i+1, x, y, DefaultRadioRange) } test.Go(time.Second * 10) @@ -106,6 +109,7 @@ func testDelManyNodes(test *otnstester.OtnsTest) { for i := 0; i < 32; i++ { test.DeleteNode(i + 1) + test.ExpectVisualizeDeleteNode(i + 1) list = test.ListNodes() test.ExpectEqual(31-i, len(list)) test.Go(time.Second * 5) diff --git a/pylibs/otns/cli/OTNS.py b/pylibs/otns/cli/OTNS.py index ba2d3949..d8262306 100644 --- a/pylibs/otns/cli/OTNS.py +++ b/pylibs/otns/cli/OTNS.py @@ -1082,27 +1082,6 @@ def set_title(self, title: str, x: int = None, y: int = None, font_size: int = N self._do_command(cmd) - def set_network_info(self, version: str = None, commit: str = None, real: bool = None) -> None: - """ - Set network info. - - :param version: The OpenThread version. - :param commit: The OpenThread commit. - :param real: If the network uses real devices. - """ - cmd = 'netinfo' - - if version is not None: - cmd += f' version "{version}"' - - if commit is not None: - cmd += f' commit "{commit}"' - - if real is not None: - cmd += f' real {1 if real else 0}' - - self._do_command(cmd) - def __enter__(self): return self diff --git a/pylibs/stress_tests/otns_performance.py b/pylibs/stress_tests/otns_performance.py index 1462f1e4..4ff3adbb 100755 --- a/pylibs/stress_tests/otns_performance.py +++ b/pylibs/stress_tests/otns_performance.py @@ -109,7 +109,7 @@ def run(self): self.result.fail_if(duration > 60, f'Execution Time ({duration}) > 60s') self.result.fail_if(counter['AlarmEvents'] > 900000, f"Too many AlarmEvents: {counter['AlarmEvents']} > 900000") - self.result.fail_if(counter['RadioEvents'] > 650000, f"Too many RadioEvents: {counter['RadioEvents']} > 650000") + self.result.fail_if(counter['RadioEvents'] > 680000, f"Too many RadioEvents: {counter['RadioEvents']} > 680000") if __name__ == '__main__': diff --git a/pylibs/unittests/test_basic.py b/pylibs/unittests/test_basic.py index d7ed7e8e..4bd4c261 100755 --- a/pylibs/unittests/test_basic.py +++ b/pylibs/unittests/test_basic.py @@ -808,6 +808,16 @@ def testSaveYamlTopology(self): ns.go(125) self.assertFormPartitions(1) + def testRealtimeMode(self): + self.tearDown() + with OTNS(otns_args=['-log', 'debug', '-realtime']) as ns: + ns.add('router') + ns.add('router') + self.assertEqual(True, ns.autogo) + self.assertEqual(1.0, ns.speed) + ns.speed = 23 + self.assertEqual(1.0, ns.speed) + if __name__ == '__main__': unittest.main() diff --git a/simulation/simulation.go b/simulation/simulation.go index 5c68640d..895961a1 100644 --- a/simulation/simulation.go +++ b/simulation/simulation.go @@ -71,22 +71,26 @@ func NewSimulation(ctx *progctx.ProgCtx, cfg *Config, dispatcherCfg *dispatcher. ctx: ctx, cfg: cfg, nodes: map[NodeId]*Node{}, - autoGo: cfg.AutoGo, + autoGo: cfg.AutoGo || cfg.Realtime, autoGoChange: make(chan bool, 1), networkInfo: visualize.DefaultNetworkInfo(), nodePlacer: NewNodeAutoPlacer(), kpiMgr: NewKpiManager(), } s.SetLogLevel(cfg.LogLevel) - s.networkInfo.Real = cfg.Real + s.networkInfo.Real = cfg.Realtime // start the dispatcher for virtual time if dispatcherCfg == nil { dispatcherCfg = dispatcher.DefaultConfig() } - dispatcherCfg.Speed = cfg.Speed - dispatcherCfg.Real = cfg.Real + if cfg.Realtime { + dispatcherCfg.Speed = 1.0 + } else { + dispatcherCfg.Speed = cfg.Speed + } + dispatcherCfg.Realtime = cfg.Realtime dispatcherCfg.DumpPackets = cfg.DumpPackets s.d = dispatcher.NewDispatcher(s.ctx, dispatcherCfg, s) @@ -219,7 +223,7 @@ func (s *Simulation) genNodeId() NodeId { func (s *Simulation) Run() { defer logger.Debugf("simulation exit.") - if s.cfg.AutoGo { + if s.autoGo { s.autoGoChange <- true } @@ -264,6 +268,9 @@ func (s *Simulation) AutoGo() bool { } func (s *Simulation) SetAutoGo(isAuto bool) { + if s.cfg.Realtime { + return + } if s.autoGo != isAuto { s.autoGoChange <- isAuto s.autoGo = isAuto @@ -447,6 +454,9 @@ func (s *Simulation) ShowDemoLegend(x int, y int, title string) { } func (s *Simulation) SetSpeed(speed float64) { + if s.cfg.Realtime { + speed = 1.0 + } s.d.SetSpeed(speed) } diff --git a/simulation/simulation_config.go b/simulation/simulation_config.go index d7745a06..e892aa36 100644 --- a/simulation/simulation_config.go +++ b/simulation/simulation_config.go @@ -47,7 +47,7 @@ type Config struct { NewNodeConfig NodeConfig Speed float64 ReadOnly bool - Real bool + Realtime bool AutoGo bool DumpPackets bool DispatcherHost string @@ -68,7 +68,7 @@ func DefaultConfig() *Config { NewNodeConfig: DefaultNodeConfig(), Speed: 1, ReadOnly: false, - Real: false, + Realtime: false, AutoGo: true, DumpPackets: false, DispatcherHost: "localhost",