Skip to content

Commit

Permalink
Feature: '-real' argument to mean realtime speed=1.0 (#137)
Browse files Browse the repository at this point in the history
* [all] change '-real' argument to -realtime , with fixed speed=1.0
* [dispatcher] remove unused code for HW nodes simulation in former 'real' mode
* [pylibs] unit test fix
* [otnstester] bugfix in OtnsTest: vizevent channel getting full - now do more emptying of channel during test; and allocate more space for viz events.
  • Loading branch information
EskoDijk authored Feb 26, 2024
1 parent 0ba2268 commit f438daa
Show file tree
Hide file tree
Showing 13 changed files with 64 additions and 145 deletions.
3 changes: 0 additions & 3 deletions cli/CmdRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
Expand Down
13 changes: 4 additions & 9 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,26 +498,21 @@ Done
Set default network and nodes info.

```shell
netinfo [version "<string>"] [commit "<string>"] [real y|n]
netinfo [version "<string>"] [commit "<string>"]
```

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
```
Expand Down
7 changes: 3 additions & 4 deletions cli/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
98 changes: 6 additions & 92 deletions dispatcher/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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{}{}
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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])
}
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions dispatcher/dispatcher_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (

type Config struct {
Speed float64
Real bool
Realtime bool
DumpPackets bool
PcapEnabled bool
PcapFrameType pcap.FrameType
Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions otns_main/otns_main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type MainArgs struct {
LogFileLevel string
WatchLevel string
OpenWeb bool
Real bool
Realtime bool
ListenAddr string
DispatcherHost string
DispatcherPort int
Expand Down Expand Up @@ -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\")")
Expand Down Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions otnstester/OtnsTest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand Down
6 changes: 5 additions & 1 deletion otnstester/tests/otns_basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
21 changes: 0 additions & 21 deletions pylibs/otns/cli/OTNS.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion pylibs/stress_tests/otns_performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__':
Expand Down
10 changes: 10 additions & 0 deletions pylibs/unittests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Loading

0 comments on commit f438daa

Please sign in to comment.