Skip to content
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
58 changes: 58 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@ Notable backward incompatible changes are the following:
adaptive rate control, either where a station transmits to several peers or
where it sends group-addressed traffic.

2. Figure result recorder bounds check

A @statistic(record=figure; targetFigure=name:N) with N equal to the number of
items the figure displays was accepted and then wrote one past the end. It is
now rejected at initialization, so a model that used the boundary index by
mistake stops with an error instead of writing into an item that does not
exist.

3. Indicator figure items

The getNumSeries() method of the IIndicatorFigure C++ interface has been
renamed to getNumItems(), and the first parameter of setValue() from series to
index. "Series" only describes what PlotFigure displays, where an item really
is a sequence of values over time; for a gauge, a counter or a thermometer the
indexed thing is a single value that happens to have an identity of its own.

getNumSeries() is kept for one release as a deprecated method, and the default
implementation of getNumItems() calls it, so an indicator figure implemented
outside INET keeps working unchanged until its author renames the override.
Overriding a deprecated method is not itself a use of its name, so the compiler
does not warn at the override; the deprecation is visible in the header and at
any remaining call site.


Notable backward compatible changes are the following:

1. IEEE 802.11 per-station rate statistics
Expand Down Expand Up @@ -57,6 +81,40 @@ Notable backward compatible changes are the following:
to the MAC address string when no host owns the address.


4. A bar chart indicator figure

The new "barChart" figure type displays several named values at once, one bar
each, with a value to color gradient and an optional autoscaled range. Unlike
the other indicator figures the number of its items is not fixed.

5. Displaying several values of a statistic at once

~StatisticVisualizerBase gained the splitBy and groupBy parameters, so every
statistic visualizer has them. splitBy splits the values a signal source emits
into several statistics, by the details object emitted with the value or by the
packet's flow; groupBy determines which statistics are displayed together as the
items of one figure, those of a source or those of a network node. The default is
neither, which is what the visualizer did before.

The figure that displays a statistic can now be given in the new figure
parameter, as the attributes of a figure the way an @figure property gives them,
which a template property along the module path could not be from an ini file.
A figure type is also looked up among the types registered with
Register_Figure(), not only as an inet::<Type>Figure class.

An indicator figure driven by the statistic visualizer is now given the value in
the display unit, the one the text label would show, rather than the raw value.
A model that configures such a figure through the propertyName template and
lists several units sees the figure follow the same unit as the label.

6. IEEE 802.11 per-peer data rate visualizer

The new ~Ieee80211RateCanvasVisualizer displays the data rate a node is using
towards each of its peers as a bar chart above the node. It is a configuration
of ~StatisticCanvasVisualizer rather than new visualizer code, and is selected
as the type of the statistic visualizer of the integrated visualizer.


INET-4.7 (July 2026) — feature release
--------------------------------------

Expand Down
51 changes: 51 additions & 0 deletions examples/visualizer/statisticbars/StatisticBarsExample.ned
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Copyright (C) 2020 OpenSim Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//


package inet.examples.visualizer.statisticbars;

import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator;
import inet.node.ethernet.Eth100M;
import inet.node.ethernet.EthernetSwitch;
import inet.node.inet.StandardHost;
import inet.visualizer.contract.IIntegratedVisualizer;

//
// Demonstrates displaying several values of a statistic at once with the
// ~StatisticVisualizer, with several UDP traffic streams flowing from the server
// to the receiver. A bar chart above the receiver shows a per-stream quantity;
// the ini file selects what, and what displays it:
//
// - `Packets` - number of packets received per stream (one bar per sink app,
// `groupBy = "networkNode"`, `statisticExpression = "count"`).
// - `Throughput` - throughput per stream (same, `statisticExpression = "throughput"`).
// - `Flow` - throughput per named flow, demultiplexed from a single signal
// by the packet's flow tag (`splitBy = "flow"`).
// - `Gauge` - throughput of a single stream on a gauge instead of a bar
// chart, to show that the figure is a configuration choice.
//
network StatisticBarsExample
{
submodules:
visualizer: <default("IntegratedCanvasVisualizer")> like IIntegratedVisualizer {
@display("p=100,80;is=s");
}
configurator: Ipv4NetworkConfigurator {
@display("p=100,160;is=s");
}
server: StandardHost {
@display("p=200,350");
}
switch: EthernetSwitch {
@display("p=450,350");
}
receiver: StandardHost {
@display("p=700,350");
}
connections:
server.ethg++ <--> Eth100M <--> switch.ethg++;
switch.ethg++ <--> Eth100M <--> receiver.ethg++;
}
130 changes: 130 additions & 0 deletions examples/visualizer/statisticbars/omnetpp.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
[General]
network = StatisticBarsExample
sim-time-limit = 10s
*.visualizer.statisticVisualizer.displayStatistics = true
# The Packets/Throughput/Flow configs give the bar chart figure a fixed maxValue;
# the matching *Autoscale configs leave it unset, so each bar chart scales to its
# own current maximum instead.

# ==========================================================================
# Grouping by network node: three UDP streams (fast/medium/slow) from the server
# to three separate sink apps on the receiver. groupBy="networkNode" displays each
# matching source module as one item, all in a single bar chart above the receiver;
# the value is the statisticExpression (count or throughput) of that module's
# packetReceived signal.
# ==========================================================================
[Config Streams]
abstract = true
*.server.numApps = 3
*.server.app[*].typename = "UdpBasicApp"
*.server.app[*].destAddresses = "receiver"
*.server.app[*].messageLength = 1000B
*.server.app[0].destPort = 1000
*.server.app[1].destPort = 1001
*.server.app[2].destPort = 1002
*.server.app[0].sendInterval = 10ms # fast stream
*.server.app[1].sendInterval = 25ms # medium stream
*.server.app[2].sendInterval = 60ms # slow stream
*.server.app[*].startTime = uniform(0s, 10ms)

*.receiver.numApps = 3
*.receiver.app[*].typename = "UdpSink"
*.receiver.app[0].localPort = 1000
*.receiver.app[1].localPort = 1001
*.receiver.app[2].localPort = 1002

*.visualizer.statisticVisualizer.groupBy = "networkNode"
*.visualizer.statisticVisualizer.signalName = "packetReceived"
*.visualizer.statisticVisualizer.sourceFilter = "*.receiver.app[*]"

[Config Packets]
extends = Streams
description = "Number of packets received per stream"
*.visualizer.statisticVisualizer.statisticExpression = "count"
*.visualizer.statisticVisualizer.figure = {type: "barChart", maxValue: 1000, valueFormat: "%.0f", barColor: "skyblue", title: "packets received"}

[Config Throughput]
extends = Streams
description = "Throughput per stream"
*.visualizer.statisticVisualizer.statisticExpression = "throughput"
*.visualizer.statisticVisualizer.statisticUnit = "bps"
*.visualizer.statisticVisualizer.unit = "Mbps"
*.visualizer.statisticVisualizer.figure = {type: "barChart", maxValue: 1, valueFormat: "%.2f", barColor: "skyblue", title: "throughput [Mbps]"}

# ==========================================================================
# Splitting by flow: the same three streams all go to ONE sink on the receiver,
# but each is tagged with a flow name (fast/medium/slow) by a FlowMeasurementStarter.
# A single FlowMeasurementRecorder at the receiver emits packetFlowMeasured for
# all three flows; splitBy="flow" demultiplexes that one signal by the packet's
# flow tag (demuxFlow) into one throughput statistic per flow, and groupBy="source"
# displays all of them on the one bar chart of that source.
# ==========================================================================
[Config Flow]
description = "Throughput per flow (demultiplexed by the packet's flow tag)"
*.server.numApps = 3
*.server.app[*].typename = "UdpApp"
*.server.app[*].source.packetLength = 1000B
*.server.app[0].source.productionInterval = 10ms # fast flow
*.server.app[1].source.productionInterval = 25ms # medium flow
*.server.app[2].source.productionInterval = 60ms # slow flow
*.server.app[*].io.destAddress = "receiver"
*.server.app[*].io.destPort = 5000
*.server.app[0].measurementStarter.typename = "FlowMeasurementStarter"
*.server.app[0].measurementStarter.flowName = "fast"
*.server.app[1].measurementStarter.typename = "FlowMeasurementStarter"
*.server.app[1].measurementStarter.flowName = "medium"
*.server.app[2].measurementStarter.typename = "FlowMeasurementStarter"
*.server.app[2].measurementStarter.flowName = "slow"

*.receiver.numApps = 1
*.receiver.app[0].typename = "UdpApp"
*.receiver.app[0].source.typename = "" # receive only
*.receiver.app[0].io.localPort = 5000
*.receiver.app[0].io.destAddress = "" # receive only (unused)
*.receiver.app[0].io.destPort = 5000 # receive only (unused)
*.receiver.app[0].measurementRecorder.typename = "FlowMeasurementRecorder"
*.receiver.app[0].measurementRecorder.flowName = "fast or medium or slow"

*.visualizer.statisticVisualizer.splitBy = "flow"
*.visualizer.statisticVisualizer.groupBy = "source"
*.visualizer.statisticVisualizer.signalName = "packetFlowMeasured"
*.visualizer.statisticVisualizer.sourceFilter = "*.receiver.app[0].measurementRecorder"
*.visualizer.statisticVisualizer.statisticExpression = "throughput(packetLength(demuxFlow))"
*.visualizer.statisticVisualizer.statisticUnit = "bps"
*.visualizer.statisticVisualizer.unit = "Mbps"
*.visualizer.statisticVisualizer.figure = {type: "barChart", maxValue: 1, valueFormat: "%.2f", barColor: "gold", title: "throughput [Mbps]"}

# ==========================================================================
# The visualizer provides the values, the figure decides how they are displayed:
# this config displays the throughput of a single stream on a gauge instead,
# without grouping several of them onto one figure.
# ==========================================================================
[Config Gauge]
extends = Streams
description = "Throughput of one stream on a gauge (any indicator figure will do)"
*.visualizer.statisticVisualizer.groupBy = "none"
*.visualizer.statisticVisualizer.sourceFilter = "*.receiver.app[0]"
*.visualizer.statisticVisualizer.statisticExpression = "throughput"
*.visualizer.statisticVisualizer.statisticUnit = "bps"
*.visualizer.statisticVisualizer.unit = "Mbps"
*.visualizer.statisticVisualizer.figure = {type: "gauge", size: [60, 60], minValue: 0, maxValue: 1, tickSize: 0.2, label: "throughput [Mbps]"}

# ==========================================================================
# Autoscale variants: same as the configs above, but with maxValue unset, so
# each bar chart scales to its own current maximum (the tallest bar always
# fills the height) instead of a fixed reference.
# ==========================================================================
[Config PacketsAutoscale]
extends = Packets
description = "Number of packets received per stream (autoscaled)"
*.visualizer.statisticVisualizer.figure = {type: "barChart", valueFormat: "%.0f", barColor: "skyblue", title: "packets received"}

[Config ThroughputAutoscale]
extends = Throughput
description = "Throughput per stream (autoscaled)"
*.visualizer.statisticVisualizer.figure = {type: "barChart", valueFormat: "%.2f", barColor: "skyblue", title: "throughput [Mbps]"}

[Config FlowAutoscale]
extends = Flow
description = "Throughput per flow (autoscaled)"
*.visualizer.statisticVisualizer.figure = {type: "barChart", valueFormat: "%.2f", barColor: "gold", title: "throughput [Mbps]"}
Loading