All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jbotsim.core.Node Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright 2008 - 2019, Arnaud Casteigts and the JBotSim contributors 
 *
 *
 * This file is part of JBotSim.
 *
 * JBotSim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JBotSim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JBotSim.  If not, see .
 *
 */
package io.jbotsim.core;

import io.jbotsim.core.event.ClockListener;
import io.jbotsim.core.event.MovementListener;

import java.util.*;

import static io.jbotsim.core.Node.PropString.*;

/**
 * 

The {@link Node} object is one of the main component of JBotSim, since it encodes an element of the graph/network. * It is used by the {@link Topology} along with the {@link Link} object to represent the graph.

* *

Any behavior modification should be implemented by subclassing this class.

*/ public class Node extends Properties implements ClockListener, Comparable { public static final Color DEFAULT_COLOR = null; public static final int DEFAULT_ICON_SIZE = 10; public static final double DEFAULT_DIRECTION = -Math.PI / 2; List mailBox = new ArrayList<>(); List sendQueue = new ArrayList<>(); HashMap outLinks = new HashMap<>(); Point coords = new Point(0, 0, 0); double direction = DEFAULT_DIRECTION; Double communicationRange = null; Double sensingRange = null; List sensedNodes = new ArrayList<>(); boolean isWirelessEnabled = true; Topology topo; Color color = null; Object label = null; Integer ID = -1; int iconSize = DEFAULT_ICON_SIZE; private boolean die = false; enum PropString { COLOR("color"), ICON("icon"), LABEL("label"), SIZE("size"); PropString(String str) { value = str; }; public String toString() { return value; } private final String value; }; /** * Returns the identifier of this node. * * @return The identifier as an integer. */ public int getID() { return ID; } /** * Sets the identifier of this node. {@link Node}s have an identifier by default, * which is the smallest available integer. * @param ID the new identifier. */ public void setID(int ID) { this.ID = ID; } /** * Returns the parent {@link Topology} of this node, if any. * * @return The parent {@link Topology}, or null if the node is orphan. */ public Topology getTopology() { return topo; } /** * Called when this node is selected (e.g. middle click in the UI) */ public void onSelection() { } /** * Override this method to re-initialise your node (e.g. your variables). * This method is also called when a node is added to the topology * (or when the topology starts, if not yet started). */ public void onStart() { } /** * This method is called just before * the node is removed from the topology. */ public void onStop() { } /** * Override this method to perform some action just before clock pulse. */ public void onPreClock() { } /** * Override this method to perform some action upon clock pulse. */ public void onClock() { } /** * Override this method to perform some action just after clock pulse. */ public void onPostClock() { } /** * Override this method to perform some action when the node moves. */ public void onMovement() { } /** * Called when this node receives a {@link Message}. * @param message the received {@link Message}. */ public void onMessage(Message message) { } /** * Called when an adjacent undirected link is added. * @param link the added {@link Link}. */ public void onLinkAdded(Link link) { } /** * Called when an adjacent undirected link is removed. * @param link the removed {@link Link}. */ public void onLinkRemoved(Link link) { } /** * Called when an adjacent directed link is added. * @param link the added {@link Link}. */ public void onDirectedLinkAdded(Link link) { } /** * Called when an adjacent directed link is removed. * @param link the removed {@link Link}. */ public void onDirectedLinkRemoved(Link link) { } /** * Called when another node is sensed for the first time. * @param node the {@link Node} that has been sensed. */ public void onSensingIn(Node node) { } /** * Called when a sensed node is no more sensed. * @param node the {@link Node} that is not sensed anymore. */ public void onSensingOut(Node node) { } /** * Request the {@link Node} to die by the end of the round. */ public void die() { die = true; } /** * Returns the dying status of the {@link Node}. * @return {@code true} if the {@link Node} is dying or already dead; returns {@code false} otherwise. */ public boolean isDying() { return die; } /** * Returns the x-coordinate of this node. * @return the x-coordinate, as a double. */ public double getX() { return coords.getX(); } /** * Returns the y-coordinate of this node. * @return the y-coordinate, as a double. */ public double getY() { return coords.getY(); } /** * Returns the z-coordinate of this node. * @return the z-coordinate, as a double. */ public double getZ() { return coords.getZ(); } /** * Returns the color of this node. * @return the {@link Node}'s {@link Color}. */ public Color getColor() { return color; } /** * Sets the color of this node. * @param color the {@link Node}'s new {@link Color}. */ public void setColor(Color color) { this.color = color; setProperty(COLOR.toString(), color); // Used for property notification } /** * Sets the icon of this node. The argument must be an absolute path to * either a file in the file system, or a resource in the application. * Examples: *
     * {@code
     *     node.setIcon("/filesystem/path/to/image");
     *     node.setIcon("/package/path/to/image");
     * }
     * 
* @param fileName a {@link String} leading to the icon. */ public void setIcon(String fileName) { setProperty(ICON.toString(), fileName); } /** * Returns the icon of the this node. * @return a {@link String} leading to the icon. */ public String getIcon() { return (String) getProperty(ICON.toString()); } /** * Returns the {@link Node}'s icon's desired size. * @return the desired size of this {@link Node}'s icon, as an integer. */ public int getIconSize() { return iconSize; } /** * Sets the {@link Node}'s icon's desired size. * @param iconSize the new desired size of this {@link Node}'s icon, as an integer. */ public void setIconSize(int iconSize) { this.iconSize = iconSize; setProperty(SIZE.toString(), iconSize); // used for property notification } /** * Returns the label of this node. * @return the label of the node, as an {@link Object}. */ public Object getLabel() { return label; } /** * Sets the label of this node. Default GUI shows it as tooltip * when the mouse cursor is held some time over the node. * @param label the {@link Object} representing the new label of the node. */ public void setLabel(Object label) { this.label = label; setProperty(LABEL.toString(), label); // Used for property notification } /** * Returns the communication range of this node (as a radius). * @return the current communication range. */ public double getCommunicationRange() { return communicationRange; } /** * Activates the wireless capabilities of this node and sets * its communication range to the specified radius. This * determines the distance up to which this node can send messages * to other nodes. * @param range the new communication range, as a double. */ public void setCommunicationRange(double range) { communicationRange = range; if (topo != null) topo.touch(this); } /** * Indicates whether this node has wireless capabilities enabled. * @return true if the wireless capabilities are enabled, * false otherwise. */ public boolean isWirelessEnabled() { return isWirelessEnabled; } /** * Enables this node's wireless capabilities. */ public void enableWireless() { setWirelessStatus(true); } /** * Disables this node's wireless capabilities. */ public void disableWireless() { setWirelessStatus(false); } /** * Set wireless capabilities status * @param enabled the new wireless status: true to enable, * false otherwise. */ public void setWirelessStatus(boolean enabled) { if (enabled == isWirelessEnabled) return; isWirelessEnabled = enabled; if (topo != null) topo.touch(this); } /** * Returns the sensing range of this node (as a radius). * @return the current sensing range. */ public double getSensingRange() { return sensingRange; } /** * Sets the sensing range of this node to the specified radius. * @param range the new sensing range, as a double. */ public void setSensingRange(double range) { sensingRange = range; notifyNodeMoved(); // for GUI refresh FIXME } /** * Returns the location of this node. * @return the current location of this node, as a {@link Point}. */ public Point getLocation() { return new Point(coords); } /** * Changes this node's location to the specified coordinates. * * @param x The abscissa of the new location. * @param y The ordinate of the new location. */ public void setLocation(double x, double y) { coords = new Point(x, y, 0); if (topo != null) topo.touch(this); notifyNodeMoved(); } /** * Changes this node's location to the specified coordinates. * * @param x The abscissa of the new location. * @param y The ordinate of the new location. * @param z The ordinate of the new location. */ public void setLocation(double x, double y, double z) { coords = new Point(x, y, z); if (topo != null) topo.touch(this); notifyNodeMoved(); } /** * Changes this node's location to the specified 2D point. * * @param loc The new location point. */ /* public void setLocation(Point loc) { setLocation(loc.getX(), loc.getY()); }*/ /** * Changes this node's location to the specified 2D point. * * @param loc The new location point. */ public void setLocation(Point loc) { setLocation(loc.getX(), loc.getY(), loc.getZ()); } /** * Changes this node's location modulo the iconSize of topology. */ public void wrapLocation() { setLocation((coords.getX() + topo.getWidth()) % topo.getWidth(), (coords.getY() + topo.getHeight()) % topo.getHeight()); } /** * Translates the location of this node by the specified coordinates. * * @param dx The abscissa component. * @param dy The ordinate component. */ public void translate(double dx, double dy) { setLocation(coords.getX() + dx, coords.getY() + dy); } /** * Translates the location of this node by the specified coordinates. * * @param dx The abscissa component. * @param dy The ordinate component. * @param dz The applicate component. */ public void translate(double dx, double dy, double dz) { setLocation(coords.getX() + dx, coords.getY() + dy, coords.getZ() + dz); } /** * Returns the current time (current round number). * @return the current time. */ public int getTime() { return topo.getTime(); } /** * Returns the current direction angle of this node (in radians). * @return the current direction. */ public double getDirection() { return direction; } /** * Sets the direction angle of this node (in radians). * * @param angle The angle in radians. */ public void setDirection(double angle) { direction = angle; notifyNodeMoved(); } /** * Sets the direction angle of this node using the specified reference * point. Only the resulting angle matters (not the particular location of * the reference point). * * @param p The reference {@link Point}. */ public void setDirection(Point p) { setDirection(Math.atan2(p.getY() - coords.getY(), (p.getX() - coords.getX()))); } /** * Sets the direction angle of this node using the specified reference * point. Only the resulting angle matters (not the particular location of * the reference point). * * @param p The reference {@link Point}. * @param opposite true if the new direction should be the opposite of the reference point. */ public void setDirection(Point p, boolean opposite) { Point p2 = (Point) p.clone(); if (opposite) p2.setLocation(2 * getX() - p.getX(), 2 * getY() - p.getY()); setDirection(p2); } /** * Translates the location of this node by one unit towards * the node's current direction. */ public void move() { move(1); } /** * Translates the location of this node by the specified distance towards * the node's current direction. * @param distance the distance by which the {@link Node} should be moved. */ public void move(double distance) { translate(Math.cos(direction) * distance, Math.sin(direction) * distance); } /** * Returns the directed link whose destination is this node and sender is * the specified node, if any such link exists. * * @param n The sender node. * @return The requested link, or null if no such link is found. */ public Link getInLinkFrom(Node n) { return topo.getLink(n, this, true); } /** * Returns the directed link whose sender is this node and destination is * the specified node, if any such link exists. * * @param n The destination node. * @return The requested link, or null if no such link is found. */ public Link getOutLinkTo(Node n) { return outLinks.get(n); } /** * Returns the undirected link whose endpoints are this node and the * specified node, if any such link exists. * * @param n The node at the opposite endpoint. * @return The requested link, or null if no such link is found. */ public Link getCommonLinkWith(Node n) { return topo.getLink(this, n); } /** * Returns a list containing all links for which this node is the * destination. The returned list can be subsequently modified without * effect on the topology. * @return the {@link List} of inbound {@link Link}s. */ public List getInLinks() { return topo.getLinks(true, this, 2); } /** * Returns a list containing all links for which this node is the * sender. The returned list can be subsequently modified without effect * on the topology. * @return the {@link List} of outbound {@link Link}s. */ public List getOutLinks() { return new ArrayList(outLinks.values()); } /** * Returns a list containing all undirected links adjacent to this node. * The returned list can be subsequently modified without effect on the * topology. * @return the {@link List} of {@link Link}s */ public List getLinks() { return getLinks(false); } /** * Returns a list containing all adjacent links of the specified type. * * @param directed true for directed, false for * undirected. The returned list can be subsequently modified without * effect on the topology. * @return the {@link List} of {@link Link}s */ public List getLinks(boolean directed) { return topo.getLinks(directed, this, 0); } /** * Returns a list containing every node serving as source for an adjacent * directed link. The returned list can be subsequently modified * without effect on the topology. * * @return A list containing the neighbors, with possible duplicates * when several links come from a same neighbor. */ public List getInNeighbors() { List neighbors = new ArrayList<>(); for (Link l : getInLinks()) neighbors.add(l.source); return neighbors; } /** * Returns a list containing every node serving as destination for an * adjacent directed link. The returned list can be subsequently * modified without effect on the topology. * * @return A list containing the neighbors, with possible duplicates * when several links go towards a same neighbor. */ public List getOutNeighbors() { ArrayList neighbors = new ArrayList<>(); for (Link l : getOutLinks()) neighbors.add(l.destination); return neighbors; } /** * Returns a list containing every node located within the sensing range * The returned list can be modified without side effect. * * @return A list containing all nodes within sensing range */ public List getSensedNodes() { ArrayList sensedNodes = new ArrayList<>(); for (Node n : topo.getNodes()) if (distance(n) < sensingRange && n != this) sensedNodes.add(n); return sensedNodes; } /** * Indicates whether this node has at least one neighbor (undirected) * * @return true if it does, false if it does not. */ public boolean hasNeighbors() { return getLinks().size() > 0; } /** * Returns a list containing every node located at the opposite endpoint * of an adjacent undirected links. The returned list can be * subsequently modified without effect on the topology. * * @return A list containing the neighbors, with possible duplicates * when several links are shared with a same neighbor. */ public List getNeighbors() { LinkedHashSet neighbors = new LinkedHashSet<>(); for (Link l : getLinks()) neighbors.add(l.getOtherEndpoint(this)); return new ArrayList<>(neighbors); } /** * Returns a list of messages representing the mailbox of this node. * The mailbox can be useful to scrutinize new messages in a non-event, * round-based way (as opposed to the onMessage() method), or to clear previous * messages (since all received messages are retained in the mailbox). The * returned list must be considered as the original copy of the node's * mailbox. * * @return the {@link List} of incoming {@link Message}s */ public List getMailbox() { return mailBox; } /** * Returns a list of the messages that this node is about to send. * * @return the {@link List} of outgoing {@link Message}s */ public List getOutbox() { return new ArrayList<>(sendQueue); } /** * Sends a message from this node to the specified destination node. * The content of the * message is specified as an object reference, to be passed 'as is' to the * destination(s). The effective transmission will occur at the * xth following clock step, where x is the * message delay specified by the static method Message.setMessageDelay * (1 by default). * * @param destination The destination node. * @param message The message to be sent. */ public void send(Node destination, Message message) { Message m = new Message(this, destination, message); sendQueue.add(m); } /** * Same as send(), but the content is directly given as parameter * (a message will be created to contain it). * * @param destination The non-null destination. * @param content The object to be sent. * * @deprecated Please use {@link #send(Node, Message)} instead. */ @Deprecated public void send(Node destination, Object content) { send(destination, new Message(content)); } /** * Same method as send(), but retries to send the message later * if the link to the destination disappeared during transmission. * (Does not work for null destinations.) * * @param destination The non-null destination. * @param message The message. */ public void sendRetry(Node destination, Message message) { assert (destination != null); Message m = new Message(message); m.retryMode = true; send(destination, m); } /** * Same as sendRetry(), but the content is directly given as parameter * (a message will be created to contain it). * * @param destination The non-null destination. * @param content The object to be sent. * * @deprecated Please use {@link #sendRetry(Node, Message)} instead. */ @Deprecated public void sendRetry(Node destination, Object content) { sendRetry(destination, new Message(content)); } /** * Sends a message to all neighbors. The content of the * message is specified as an object reference, to be passed 'as is' to the * destination(s). The effective transmission will occur at the * xth following clock step, where x is the * message delay specified by the static method Message.setMessageDelay * (1 by default). * * @param message The message to be sent. */ public void sendAll(Message message) { send(null, message); } /** * Same as sendAll(), but the content is directly given as parameter * (a message will be created to contain it). * * @param content The object to be sent. * * @deprecated Please use {@link #sendAll(Message)} instead. */ @Deprecated public void sendAll(Object content) { send(null, new Message(content)); } /** * Returns the distance between this node and the specified node. * * @param n The other node. * @return the distance, as a double. */ public double distance(Node n) { return coords.distance(n.coords); } /** * Returns the distance between this node and the specified 2D location. * * @param p The location (as a point). * @return the distance, as a double. */ /* public double distance(Point p) { return coords.distance(p.getX(), p.getY(), 0); }*/ /** * Returns the distance between this node and the specified 2D location. * * @param x The abscissa of the point to which the distance is measured. * @param y The ordinate of the point to which the distance is measured. * @return the distance, as a double. */ public double distance(double x, double y) { return coords.distance(x, y, 0); } /** * Returns the distance between this node and the specified 3D location. * * @param p The location (as a 3D point). * @return the distance, as a double. */ public double distance(Point p) { return coords.distance(p.getX(), p.getY(), p.getZ()); } /** * Returns the distance between this node and the specified 3D location. * * @param x The abscissa of the point to which the distance is measured. * @param y The ordinate of the point to which the distance is measured. * @param z The applicate of the point to which the distance is measured. * @return the distance, as a double. */ public double distance(double x, double y, double z) { return coords.distance(x, y, z); } protected void notifyNodeMoved() { onMovement(); if (topo != null) for (MovementListener ml : new ArrayList<>(topo.movementListeners)) ml.onMovement(this); } @Override public int compareTo(Node o) { return (toString().compareTo(o.toString())); } /** * Returns a string representation of this node. */ public String toString() { if (label == null) return ID.toString(); else return label.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy