Commit cb05b09c authored by Clemens Krug's avatar Clemens Krug
Browse files

Reworked the Node Debug Monitor

+ Made the node debug a standalone component similar to the Monitor class positioned properly inside the api and peerfact.
+ Added filtering capabilities
+ Added logging in some classes deep in the architecture the provide node information
parent 6cea80e3
package de.tud.kom.p2psim.api.common;
/**
* Created by Clemens on 02.04.2017.
*/
public interface NodeDebugUpdateListener
{
}
package de.tud.kom.p2psim.impl.common;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.NodeDebugVisualisation;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.core.NodeDebugMonitorComponent;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
* Default node debug monitor. Provides logging capabilities and data access as well
* as the option to define filters on which information should be logged.
* Per node information can be shown by simply clicking on the desired node.
* Filters can be used as blacklist or as whitelist and
* are specified via the xml config. <BR><BR>
*
* How to define Filters:<BR>
* The standard XML configuration looks like this:<BR><BR>
* &lt;DebugMonitor class="de.tud.kom.p2psim.impl.common.DefaultNodeDebugMonitor" static="getInstance" enableVis="$ENABLE_DEBUG_VIS"&gt;<BR>
* &#09;&lt;PackageFilter class="de.tud.kom.p2psim.impl.common.PackageFilter"<BR>
* &#09;&#09;type="EXCLUDE"<BR>
* &#09;&#09;packageName="de.tudarmstadt.maki.simonstrator.peerfact.application.resAlloc.user"/&gt<BR>
* &lt;/DebugMonitor&gt;<BR><BR>
*
* Any package or class can be specified as filter and all
* subpackages and classes will be filtered accordingly. For types you can use 'EXCLUDE' or 'INCLUDE'
* depending on if you want to use whitelisting or blacklisting. Multiple
* &lt;PackageFilter&gt; tags can be defined, however you can't use INCLUDE and EXCLUDE simultaneously.
*/
public class DefaultNodeDebugMonitor implements NodeDebugMonitorComponent
{
private static DefaultNodeDebugMonitor instance;
private HashMap<INodeID, HashMap<Class, HashMap<String, Object>>> data = new HashMap<>();
private LinkedList<NodeDebugUpdateListener> listeners = new LinkedList<>();
//Marker to prevent creation of two visualisation components
private boolean visAlreadyEnabled = false;
//Marker determining if blacklisting or whitelisting is enabled
private boolean filterInitialised = false;
private boolean whitelist = false;
private LinkedList<PackageFilter> filteredPackages = new LinkedList<>();
private HashMap<Class, Boolean> filteredClasses = new HashMap<>();
public static DefaultNodeDebugMonitor getInstance()
{
if(instance == null)
{
instance = new DefaultNodeDebugMonitor();
}
return instance;
}
@Override
public void update(Class<?> subject, INodeID nodeID, String entry, Object value)
{
if(filteredClasses.containsKey(subject))
{
//If Whitelisting and class not contained in filter
if(whitelist && !filteredClasses.get(subject)) return;
//If blacklisting and class contained in filter.
if(!whitelist && filteredClasses.get(subject)) return;
}
HashMap<Class, HashMap<String, Object>> nodeData;
if(data.containsKey(nodeID)) nodeData = data.get(nodeID);
else {
nodeData = new HashMap<>();
data.put(nodeID, nodeData);
}
HashMap<String, Object> nodeClassData;
if(nodeData.containsKey(subject)) nodeClassData = nodeData.get(subject);
else
{
//First update for this class. Check if the class needs to be filtered.
boolean classFiltered = isFiltered(subject);
filteredClasses.put(subject, classFiltered);
//If the class is excluded, don't add the information and return.
if((whitelist && !classFiltered) || (!whitelist && classFiltered) ) return;
nodeClassData = new HashMap<>();
nodeData.put(subject, nodeClassData);
}
nodeClassData.put(entry, value);
listeners.forEach(l -> l.onNodeDebugUpdate(subject, nodeID, entry, value));
}
@Override
public Map<Class, HashMap<String, Object>> getNodeData(INodeID nodeID) {
if(data.containsKey(nodeID)) return data.get(nodeID);
else return new HashMap<>();
}
@Override
public void addUpdateListener(NodeDebugUpdateListener listener) {
listeners.add(listener);
}
@Override
public void removeUpdateListener(NodeDebugUpdateListener listener) {
listeners.remove(listener);
}
@Override
public void setEnableVis(boolean enabled) {
if(enabled && !visAlreadyEnabled) new NodeDebugVisualisation();
}
/**
* Adds a filter to the debug monitor. Should only be used via XML config and
* is not meant to be only called while simulation setup.
* @param filter the filter to add
*/
public void setPackageFilter(PackageFilter filter)
{
//First entry sets whitelisting/blacklisting
if(!filterInitialised)
{
filterInitialised = true;
whitelist = filter.isWhitelist();
}
// If the added type is not conform with the type of listing, an error is thrown.
if(filter.isWhitelist() != whitelist) throw new AssertionError("Can't use Includes/Excludes simultaneously!");
//Don't save filters for packages where a parent package is already filtered.
for(PackageFilter pf : filteredPackages)
{
if(filter.subPackageOf(pf)) return;
if(pf.subPackageOf(filter))
{
filteredPackages.remove(pf);
filteredPackages.add(filter);
return;
}
}
filteredPackages.add(filter);
}
/**
* Check if a class is contained in the filter.
* @param c the class to check
* @return true if contained, false otherwise.
*/
private boolean isFiltered(Class c)
{
for(PackageFilter pf : filteredPackages)
{
if(pf.containsClass(c)) return true;
}
return false;
}
}
package de.tud.kom.p2psim.impl.common;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* Created by Clemens on 29.04.2017.
*/
public class PackageFilter
{
private String packageName;
private TYPE type;
@XMLConfigurableConstructor({"packageName", "type"})
public PackageFilter(String packageName, String type)
{
this.packageName = packageName;
this.type = TYPE.valueOf(type.toUpperCase());
}
private String getPackageName()
{
return packageName;
}
public boolean isWhitelist()
{
return type == TYPE.INCLUDE;
}
public boolean subPackageOf(PackageFilter filter)
{
return filter.getPackageName().contains(packageName);
}
public boolean containsClass(Class c)
{
return c.getName().contains(packageName);
}
public enum TYPE
{
INCLUDE,
EXCLUDE
}
}
...@@ -38,13 +38,7 @@ import de.tud.kom.p2psim.api.topology.movement.MovementModel; ...@@ -38,13 +38,7 @@ import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel; import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.api.topology.views.TopologyView; import de.tud.kom.p2psim.api.topology.views.TopologyView;
import de.tud.kom.p2psim.impl.simengine.Simulator; import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tudarmstadt.maki.simonstrator.api.Event; import de.tudarmstadt.maki.simonstrator.api.*;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Graphs;
import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Oracle;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.common.graph.Graph; import de.tudarmstadt.maki.simonstrator.api.common.graph.Graph;
import de.tudarmstadt.maki.simonstrator.api.common.graph.IEdge; import de.tudarmstadt.maki.simonstrator.api.common.graph.IEdge;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INode; import de.tudarmstadt.maki.simonstrator.api.common.graph.INode;
...@@ -263,6 +257,8 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -263,6 +257,8 @@ public class DefaultTopologyComponent implements TopologyComponent {
@Override @Override
public void updateCurrentLocation(Location location) { public void updateCurrentLocation(Location location) {
position.set(location); position.set(location);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Current Location", location);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Distance to target", location.distanceTo(movementModel.getTargetLocation(this)));
// notify "non-request" listeners // notify "non-request" listeners
for (LocationListener locationListener : listeners) { for (LocationListener locationListener : listeners) {
locationListener.onLocationChanged(getHost(), getLastLocation()); locationListener.onLocationChanged(getHost(), getLastLocation());
...@@ -273,6 +269,8 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -273,6 +269,8 @@ public class DefaultTopologyComponent implements TopologyComponent {
public void setTargetAttractionPoint(AttractionPoint targetAttractionPoint) public void setTargetAttractionPoint(AttractionPoint targetAttractionPoint)
throws UnsupportedOperationException { throws UnsupportedOperationException {
movementModel.changeTargetLocation(this, targetAttractionPoint); movementModel.changeTargetLocation(this, targetAttractionPoint);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Target Location", targetAttractionPoint);
} }
@Override @Override
......
...@@ -8,6 +8,7 @@ import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitio ...@@ -8,6 +8,7 @@ import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitio
import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
import java.util.HashMap; import java.util.HashMap;
......
package de.tud.kom.p2psim.impl.topology.views.visualization.ui;
import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Vector;
/**
* Created by Clemens on 20.02.2017.
*/
public class DebugNodeView extends JFrame
{
private JPanel panel;
private JTable table;
private DefaultTableModel model;
private long hostId;
public DebugNodeView(long hostId)
{
this.hostId = hostId;
setTitle("Host: " + hostId);
SwingUtilities.invokeLater(this::createGUI);
}
private void createGUI()
{
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setPreferredSize(new Dimension(300,300));
Map<Class, HashMap<String, Object>> nodeData = NodeDebugMonitor.getNodeData(INodeID.get(hostId));
Vector tableData = convertToTableVector(nodeData);
Vector<String> columns = new Vector<>();
columns.add("Klasse");
columns.add("Beschreibung");
columns.add("Wert");
model = new DefaultTableModel(tableData, columns);
table = new JTable(model);
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);
panel.add(scrollPane, BorderLayout.NORTH);
getContentPane().add(panel);
pack();
setVisible(true);
}
public void update(Class subject, String entry, String value)
{
int rowIndex = findEntry(subject.getSimpleName(), entry);
if(rowIndex >= 0)
{
((Vector) model.getDataVector().get(rowIndex)).set(2, value);
model.fireTableDataChanged();
}
else
{
String[] data = {subject.getSimpleName(), entry, value};
model.addRow(data);
}
}
private int findEntry(String subject, String entry)
{
Vector data = model.getDataVector();
for(int i = 0; i < data.size(); i++)
{
Vector v = (Vector) data.get(i);
if(v.get(0).equals(subject) && v.get(1).equals(entry))
{
return i;
}
}
return -1;
}
private Vector<Vector<String>> convertToTableVector(Map<Class, HashMap<String, Object>> nodeData)
{
Vector<Vector<String>> tableData = new Vector<>();
for (Map.Entry<Class, HashMap<String, Object>> classData : nodeData.entrySet())
{
String classname = classData.getKey().getSimpleName();
for(Map.Entry<String, Object> entry : classData.getValue().entrySet())
{
String value;
if (entry.getValue() == null) value = "NULL";
else value = entry.getValue().toString();
Vector<String> entryVector = new Vector<>();
entryVector.add(classname);
entryVector.add(entry.getKey());
entryVector.add(value);
tableData.add(entryVector);
}
}
return tableData;
}
}
package de.tud.kom.p2psim.impl.topology.views.visualization.ui;
import de.tud.kom.p2psim.api.common.NodeDebugUpdateListener;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.NodeVisInteractionListener;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.core.NodeDebugMonitorComponent;
import java.util.HashMap;
/**
* Created by Clemens on 02.04.2017.
*/
public class NodeDebugVisualisation implements NodeVisInteractionListener, NodeDebugMonitorComponent.NodeDebugUpdateListener
{
private HashMap<Long, DebugNodeView> frames = new HashMap<>();
public NodeDebugVisualisation()
{
Event.scheduleImmediately((content, type) -> {
NodeDebugMonitor.addUpdateListener(NodeDebugVisualisation.this);
VisualizationTopologyView.VisualizationInjector.addInteractionListener(NodeDebugVisualisation.this);
}, null, 0);
}
@Override
public void onHostClick(long hostID, boolean isActive)
{
if(isActive)
{
frames.put(hostID, new DebugNodeView(hostID));
}
else
{
DebugNodeView view = frames.remove(hostID);
view.dispose();
}
}
@Override
public void onNodeDebugUpdate(Class subject, INodeID nodeId, String entry, Object value)
{
String string;
if(value == null) string = "NULL";
else string = value.toString();
if(frames.containsKey(nodeId.value()))
{
frames.get(nodeId.value()).update(subject, entry, string);
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment