Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] Add energy charts to the web interface - Part 10 of RTEA #366

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion cli/CmdRunner.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020, The OTNS Authors.
// Copyright (c) 2022, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -215,6 +215,8 @@ func (rt *CmdRunner) execute(cmd *Command, output io.Writer) {
rt.executeWeb(cc, cc.Web)
} else if cmd.NetInfo != nil {
rt.executeNetInfo(cc, cc.NetInfo)
} else if cmd.Energy != nil {
rt.executeEnergy(cc, cc.Energy)
} else {
simplelogger.Panicf("unimplemented command: %#v", cmd)
}
Expand Down Expand Up @@ -769,6 +771,17 @@ func (rt *CmdRunner) executeCoaps(cc *CommandContext, cmd *CoapsCmd) {
}
}

func (rt *CmdRunner) executeEnergy(cc *CommandContext, energy *EnergyCmd) {
if energy.Save != nil {
rt.postAsyncWait(func(sim *simulation.Simulation) {
sim.GetEnergyAnalyser().SaveEnergyDataToFile(energy.Name, sim.Dispatcher().CurTime)
})
} else {
cc.outputf("energy <command>\n")
cc.outputf("\tsave [output name]\n")
}
}

func NewCmdRunner(ctx *progctx.ProgCtx, sim *simulation.Simulation) *CmdRunner {
cr := &CmdRunner{
ctx: ctx,
Expand Down
15 changes: 14 additions & 1 deletion cli/ast.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020, The OTNS Authors.
// Copyright (c) 2022, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -60,6 +60,7 @@ type Command struct {
Speed *SpeedCmd `| @@` //nolint
Title *TitleCmd `| @@` //nolint
Web *WebCmd `| @@` //nolint
Energy *EnergyCmd `| @@` //nolint
}

//noinspection GoStructTag
Expand Down Expand Up @@ -327,6 +328,18 @@ type WebCmd struct {
Cmd struct{} `"web"` //nolint
}

//noinspection GoStructTag
type EnergyCmd struct {
Cmd struct{} `"energy"` //nolint
Save *SaveFlag `( @@ )?` //nolint
Name string `@String?` //nolint
}

//noinspection GoStructTag
type SaveFlag struct {
Dummy struct{} `"save"` //nolint
}

//noinspection GoStructTag
type RadioCmd struct {
Cmd struct{} `"radio"` //nolint
Expand Down
8 changes: 7 additions & 1 deletion cmd/otns-replay/grpc_service.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020, The OTNS Authors.
// Copyright (c) 2022, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -74,6 +74,12 @@ waitloop:
return nil
}

func (gs *grpcService) EnergyReport(req *pb.VisualizeRequest, stream pb.VisualizeGrpcService_EnergyReportServer) error {
//TODO: implement energy report for replay, if it fits.
var err error
return err
}

func (gs *grpcService) Command(context.Context, *pb.CommandRequest) (*pb.CommandResponse, error) {
// TODO: implement some commands for replay (e.g. speed)
return nil, errors.Errorf("can not run command on replay")
Expand Down
2 changes: 1 addition & 1 deletion cmd/otns-replay/otns_replay.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020, The OTNS Authors.
// Copyright (c) 2022, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down
170 changes: 147 additions & 23 deletions dispatcher/Node.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020, The OTNS Authors.
// Copyright (c) 2022, The OTNS Authors.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -40,6 +40,8 @@ import (
const (
maxPingResultCount = 1000
maxJoinResultCount = 1000
minChannel = 11
maxChannel = 26
)

type pingRequest struct {
Expand All @@ -65,15 +67,22 @@ type JoinResult struct {
}

type Node struct {
D *Dispatcher
Id NodeId
X, Y int
PartitionId uint32
ExtAddr uint64
Rloc16 uint16
CreateTime uint64
CurTime uint64
Role OtDeviceRole
D *Dispatcher
Id NodeId
X, Y int
PartitionId uint32
ExtAddr uint64
Rloc16 uint16
CreateTime uint64
CurTime uint64
Role OtDeviceRole
RadioState RadioStates
RadioChannel uint8
radioLockState bool
rxBusyUntil []uint64
isInvalidReception []bool
isWaitingAck bool
waitAckSN uint8

peerAddr *net.UDPAddr
failureCtrl *FailureCtrl
Expand All @@ -90,18 +99,23 @@ func newNode(d *Dispatcher, nodeid NodeId, x, y int, radioRange int) *Node {
simplelogger.AssertTrue(radioRange >= 0)

nc := &Node{
D: d,
Id: nodeid,
CurTime: d.CurTime,
CreateTime: d.CurTime,
X: x,
Y: y,
ExtAddr: InvalidExtAddr,
Rloc16: threadconst.InvalidRloc16,
Role: OtDeviceRoleDisabled,
peerAddr: nil, // peer address will be set when the first event is received
radioRange: radioRange,
joinerState: OtJoinerStateIdle,
D: d,
Id: nodeid,
CurTime: d.CurTime,
CreateTime: d.CurTime,
X: x,
Y: y,
ExtAddr: InvalidExtAddr,
Rloc16: threadconst.InvalidRloc16,
Role: OtDeviceRoleDisabled,
peerAddr: nil, // peer address will be set when the first event is received
radioRange: radioRange,
joinerState: OtJoinerStateIdle,
RadioChannel: minChannel,
radioLockState: false,
isInvalidReception: make([]bool, maxChannel-minChannel+1), // one for each channel (11-26)
rxBusyUntil: make([]uint64, maxChannel-minChannel+1),
isWaitingAck: false,
}

nc.failureCtrl = newFailureCtrl(nc, NonFailTime)
Expand All @@ -116,14 +130,41 @@ func (node *Node) String() string {
func (node *Node) Send(elapsed uint64, data []byte) {
msg := make([]byte, len(data)+11)
binary.LittleEndian.PutUint64(msg[:8], elapsed)
msg[8] = eventTypeRadioReceived
if node.D.IsCollisionAware { // TODO: Delete after OT has been merged with the new timing PR
msg[8] = eventTypeRadioComm
} else {
msg[8] = eventTypeRadioReceived
}

binary.LittleEndian.PutUint16(msg[9:11], uint16(len(data)))
n := copy(msg[11:], data)
simplelogger.AssertTrue(n == len(data))

node.SendMessage(msg)
}

func (node *Node) SendTxDoneSignal(elapsed uint64, seq uint8) {
msg := make([]byte, 12)
binary.LittleEndian.PutUint64(msg[:8], elapsed)
msg[8] = eventTypeRadioTxDone

binary.LittleEndian.PutUint16(msg[9:11], uint16(1))
msg[11] = seq

node.SendMessage(msg)
}

func (node *Node) SendChannelActivity(channel uint8, value int8, elapsed uint64) {
msg := make([]byte, 13)
binary.LittleEndian.PutUint64(msg[:8], elapsed)
msg[8] = eventTypeChannelActivity

binary.LittleEndian.PutUint16(msg[9:11], uint16(9))
msg[11] = channel
msg[12] = uint8(value)
node.SendMessage(msg)
}

func (node *Node) SendMessage(msg []byte) {
if node.peerAddr != nil {
_, _ = node.D.udpln.WriteToUDP(msg, node.peerAddr)
Expand Down Expand Up @@ -300,3 +341,86 @@ func (node *Node) addJoinResult(js *joinerSession) {
node.joinResults = node.joinResults[1:]
}
}

func (node *Node) IsReceptionSuccess(channel uint8) bool {
return !node.isFailed &&
node.RadioState == RadioRx &&
node.RadioChannel == channel &&
node.D.CurTime >= node.rxBusyUntil[channel-minChannel] &&
!node.isInvalidReception[channel-minChannel]
}

func (node *Node) IsCollisionEvent(src *Node) bool {
return node.Id != src.Id && // Needed check
node.RadioChannel == src.RadioChannel &&
node.D.CurTime < node.rxBusyUntil[src.RadioChannel-minChannel] &&
node.isInvalidReception[src.RadioChannel-minChannel]
}

func (node *Node) IsWaitingAck(seq uint8) bool {
return node.isWaitingAck && node.waitAckSN == seq
}

func (node *Node) InformAckReceived(seq uint8) {
if node.IsWaitingAck(seq) {
node.isWaitingAck = false
node.waitAckSN = 0
}
}

func (node *Node) ReceivePacket(channel uint8, until uint64) {
if !node.isInvalidReception[channel-minChannel] {
if node.rxBusyUntil[channel-minChannel] > 0 || node.RadioChannel != channel || node.isFailed {
node.isInvalidReception[channel-minChannel] = true
}
}

if until > node.rxBusyUntil[channel-minChannel] {
node.rxBusyUntil[channel-minChannel] = until
}
}

func (node *Node) IsChannelBusy(channel uint8) bool {
return !node.isFailed && node.rxBusyUntil[channel-minChannel] > 0
}

func (node *Node) SetInvalidReception() {
node.isInvalidReception[node.RadioChannel-minChannel] = true
}

func (node *Node) UpdateCollisionCondition() {
for i := 0; i < maxChannel-minChannel+1; i++ {
if node.rxBusyUntil[i] <= node.D.CurTime {
node.isInvalidReception[i] = false
node.rxBusyUntil[i] = 0
}
}
}

func (node *Node) SetRadioStateFromString(s string, timestamp uint64) {
radioEnergy := node.D.energyAnalyser.GetNode(node.Id)

simplelogger.AssertNotNil(radioEnergy)
simplelogger.AssertFalse(node.radioLockState, "radio state was locked")

var state RadioStates
switch s {
case "off":
state = RadioDisabled
case "sleep":
state = RadioSleep
case "tx":
state = RadioTx
case "rx":
state = RadioRx
default:
simplelogger.Panicf("unknown radio state: %s", s)
}

node.RadioState = state
radioEnergy.SetRadioState(state, timestamp)
}

func (node *Node) LockRadioState(lock bool) {
node.radioLockState = lock
}
Loading