Drop files here!
WNSWE is a purely client-side tool for simulating abstract networks of communicating nodes.
Created by Lucas Schröder
WNSWE uses the following external libraries:
The Simulator can be used to allow nodes of an undirected, weighted graph to execute Python code and to send and receive messages from each other.
Every node is an object of a specific Python class and can call methods of that class. Additionally, each node has its own run method, that is called at the start of the simulation to control the behavior of the node.
Messages between nodes contain JSON data, that can be viewed and modified while the simulation is running. When sending a message from Python, a dictionary has to be passed, which then is serialized into the message JSON. Thus, it may only contain other dictionaries, lists, strings, numbers or booleans.
When the simulatior has just been started, it will be inactive. By clicking on the Start button in the header, the simulation can be started and then paused and resumed with the corresponding button. While the simulation is active (running or paused), changes from the Python code of classes or run methods do not become active. For that, the simulation has to be reset by clicking on the Reset button in the header, and after that it has to be started again.
The header contains multiple controls to control the simulator:
This area visually shows the state of the simulated network
By clicking and dragging with the left mouse butten the view can be moved around. Scrolling with the mouse wheel allows for zooming.
By clicking with right mouse button a context menu can be opened, which also contains an option to reset the view.
In the top left corner of the simulation area the current time since the start of the simulation is shown.
Clicking with the left mouse button allows the selection of nodes, connections or messages. By holding the Ctrl key, multiple elements of the same kind can be selected together.
Details of the currently selected elements are shown in the sidebar. Changes to these elements can also be made there.
By clicking with the right mouse button, a context menu can be opened. Depending on where the menu is opened, different options are available:
Selected Nodes can be clicked and dragged with the left mouse button to change their position.
The sidebar shows various information depending on the current selection:
Python classes for nodes must inherit from the Endpoint class. This class contains the methods required for sending and receiving of messages, as well as additional Attributes. Every node has its own run method, which controls the behavior of that node. If this is not desired, i.e. if every node of the same class should act the same (for instance a hub or switch), then an infinitely looped method can be definied within the class and be called from the run method. Configuration of nodes (e.g. addresses) can also be done via this run method.
In order for the simulation to continue running at the same time as the nodes are executing their code, asynchronous methods are used. These methods are not defined with def
as usual, but wihth async def
instead. Additionally, these methods can not be called directly via self.method()
, but instead have to becalled with await self.method()
. Asynchronous methods can only be called from other asynchronous methods, though they may also call synchronous methods if desired.
The Endpoint class provides all methods that a node requires in order to send or receive messages.
Asynchronous methods can only be called from other asynchronous methods. Additionally, they must be called with await self.method()
, as otherwise they are not execute.
async receive(self, timeout=-1)
With msg, adr = await self.recieve()
it is possible for a node to receive a message. The methods waits until a message has been receives. It returns a message object and the UUID of the sender.
With timeout a time can be specified, which determines how long the method waits for a messagee. If the timeout is less than 0, it will wait until a message is received. If the timeout is greater or equal to 0, then it waits until either a message arrives or the time is up. If a message arrives, the function returns normally. If the time runs out, a TimeoutError
exception will be thrown.
Message
ObjectMessage
Objects contain the information of a recieved message within attributes.
data
: the contents of the messagecolor
: the color of the messagesent_timestampt
: the time, at which the message was sent.async send(self, uuid, data, message_class=Message)
With await self.send(adr, data)
a message can be send to a neighboring node, identified by its UUID. The UUID of the receiver can be determined when a message has been received from that node, or through self.get_neighbors()
If the node is not connected with the receiver node, a ConnectionError
exception will be thrown.
The data that that should be sent must be JSON serializable, i.e. it must be dictionary, list, string, number or boolean, and it can only contain this kind of data.
With the message_class
parameter, a class or instance of a class can be passed, from which the class of the message will be derived. This allows to specify and use specific kinds of messages, which can have different colors or speeds.
async broadcast(self, data, exclude=[], message_class=Message)
With await self.broadcast(data)
a message can be sent to all neighbors of a node. Internally, this method calls the send()
method for each neighbor. With exclude
, a list of neighbor UUIDs can be passed, which will not be included in the broadcast. This can be used for instance if a Hub broadcasts a message to everyone except the original sender.
Synchronous methods are called with self.method()
like usual, even from within an asynchronous method.
print(self, *objects, sep=" ", end="\n", file=None, flush=False
)With self.print("text")
, text can be printed. The text will appear both in the output of the node itself, and in the global log. The normal print()
should not be used, since it will only output to the browser console.
get_neighbors(self)
With neighbors = self.get_neighbors()
a list of the UUIDs of the currently neighboring nodes can be retrieved.
Attributes of the Endpoint class can read or written to.
color
This contains the current color of the node. With self.color = Color.Red
, the color can be changed while the simulation is running. The color may either be:
"white"
or "lightgoldenrodeyellow"
)"#aaffaa"
)Color
enum (e.g. Color.Red
or Color.LightBlue
)display_name
This attribute contains the current name of the node. With self.display_name = "Name"
, the name of the node can be changed while the simulation is running.
These Attributes may only be read, but not changed.
_uuid
Contains the uuid of a node.
BaseMessage
Messages make use of a class for storing the meta data of the Message. Color and speed of the message are taken from this class. Messages classes must inherit from the BaseMessage class. In the __init__()
method the color and speed may be specified via self.color
and self.speed
, respectively.
color
The color of a message. May contain the same values as the color of a node:"white"
or "lightgoldenrodeyellow"
)"#aaffaa"
)Color
enum (e.g. Color.Red
or Color.LightBlue
)speed
: a factor, with which the speed of a message is multiplied on a connection. 1.0
is normal speed, 0.5
is half speed and 2.0
is double the normal speed. Values less than or equal to 0 are ignored.These functions are available independently of the class.
get_node_name(uuid)
Returns the current name of the node identified by the UUID.
get_time()
Returns the current time since start of the simulation, in seconds.
async sleep(seconds)
With await sleep(30)
the code may sleep for the specified number of seconds. The time may be given as a float, for instance àwait sleep(0.5)
.
The following list shows all the predefined colors within the Color
enum: