Commit a0faebc8 authored by Leigh Stoller's avatar Leigh Stoller

Make the new node selector applet live, since no more feedback was

forthcoming ...
parent 12eeadb1
......@@ -14,8 +14,7 @@ include $(OBJDIR)/Makeconf
ifeq ($(JAVAC),)
JARS =
else
JARS = $(TESTBED_SRCDIR)/www/robotrack/tracker.jar \
$(TESTBED_SRCDIR)/www/robotrack/selector.jar
JARS = $(TESTBED_SRCDIR)/www/robotrack/tracker.jar
endif
all:
......
......@@ -30,9 +30,11 @@ public class NodeSelect extends JApplet {
private Selector selector;
int myHeight, myWidth;
String uid, auth, pid, eid;
double pixels_per_meter = 1.0;
double pixels_per_meter = 100.0;
URL urlServer;
static final DecimalFormat FORMATTER = new DecimalFormat("0.00");
static final int INCH =
Toolkit.getDefaultToolkit().getScreenResolution();
/* From the floormap generation code. */
int X_ADJUST = 60;
......@@ -43,6 +45,7 @@ public class NodeSelect extends JApplet {
int FONT_WIDTH = 10;
Font OurFont = null;
Font BoldFont = null;
Font RulerFont = null;
// The width of the list boxes.
int LISTBOX_WIDTH = 150;
......@@ -50,6 +53,10 @@ public class NodeSelect extends JApplet {
// For zooming ...
double scale = 1.0;
// rulers
int HORIZONTAL = 0;
int VERTICAL = 1;
/*
* A little class to hold info we need to get data from the server
* about buildings, floors, nodes.
......@@ -150,8 +157,9 @@ public class NodeSelect extends JApplet {
myHeight = appletSize.height;
myWidth = appletSize.width;
}
OurFont = new Font("Helvetica", Font.BOLD, 16);
BoldFont = new Font("Helvetica", Font.BOLD, 20);
OurFont = new Font("Helvetica", Font.BOLD, 12);
BoldFont = new Font("Helvetica", Font.BOLD, 20);
RulerFont = new Font("SansSerif", Font.PLAIN, 10);
/*
* Now add my one object to the contentpane.
......@@ -559,26 +567,28 @@ public class NodeSelect extends JApplet {
this.revalidate();
}
public void mousePressed(MouseEvent e) {
int button = e.getButton();
int modifier = e.getModifiersEx();
int x = e.getX() + xoff;
int y = e.getY() - yoff;
Node node = null;
int button = e.getButton();
int modifier = e.getModifiersEx();
int x = e.getX() + xoff;
int y = e.getY() - yoff;
PhysNode pnode = null;
System.out.println("Clicked on " + e.getX() + "," + e.getY());
System.out.println("Clicked on " + x + "," + y);
if (x > 0 && y > 0 && x <= width && y <= height)
node = FindNode(this, (int)(x / scale), (int)(y / scale));
pnode = (PhysNode)
FindNode(this, (int)(x / scale), (int)(y / scale));
if (!e.isPopupTrigger()) {
if (node != null) {
SelectNode(node, modifier);
if (pnode != null) {
if (!pnode.allocated)
SelectNode(pnode, modifier);
return;
}
}
// Forward to outer event handler, including the node
selector.doPopupMenu(e, node);
selector.doPopupMenu(e, pnode);
}
public void mouseReleased(MouseEvent e) {
PhysMapPanel.mouseReleased(e);
......@@ -688,7 +698,7 @@ public class NodeSelect extends JApplet {
ListPanel.add(ToplistScroller);
// add a label for the lower list box
ListPanel.add(new JLabel("Selected Nodes"));
ListPanel.add(new JLabel("Assigned Nodes"));
// And add the lower listbox.
ListPanel.add(BotlistScroller);
......@@ -790,6 +800,9 @@ public class NodeSelect extends JApplet {
DefaultListModel model;
PhysNode pnode = (PhysNode) a.nextElement();
if (pnode.allocated)
continue;
if (pnode.selected)
model = (DefaultListModel) SelectList.getModel();
else
......@@ -801,11 +814,33 @@ public class NodeSelect extends JApplet {
}
/*
* Create a surrounding scroll pane.
* We need to force the physmappanel to layout now, so we
* figure out how big the ruler bars need to be. If we do not
* do that, the rulers will be 0 sized, and really hard to see!
*/
PhysMapPanel.getLayout().layoutContainer(PhysMapPanel);
Dimension physmapdim =
PhysMapPanel.getLayout().minimumLayoutSize(PhysMapPanel);
/*
* Create a surrounding scroll pane.
*/
PhysMapScrollPane = new JScrollPane(PhysMapPanel);
PhysMapScrollPane.setPreferredSize(new Dimension(mapwidth,
mapheight));
// Create the row and column headers (rulers, sortof).
Ruler columnView = new Ruler(HORIZONTAL);
Ruler rowView = new Ruler(VERTICAL);
// Make sure the rulers know how big they need to be.
columnView.setPreferredWidth(physmapdim.width);
rowView.setPreferredHeight(physmapdim.height);
PhysMapScrollPane.setColumnHeaderView(columnView);
PhysMapScrollPane.setRowHeaderView(rowView);
// Now we can add the scrollpane.
MapPanel.add(PhysMapScrollPane);
// Add add our two inner panels to the outer panel
......@@ -1487,11 +1522,23 @@ public class NodeSelect extends JApplet {
map.rescale();
}
// Force everything to layout again before getSize().
// Force everything to layout again.
revalidate();
validate();
repaint();
// Tell rulers to recompute their size.
Ruler ruler =
(Ruler) PhysMapScrollPane.getColumnHeader().getView();
ruler.reset();
ruler =
(Ruler) PhysMapScrollPane.getRowHeader().getView();
ruler.reset();
// Force everything to layout again.
revalidate();
validate();
/*
* Okay scale P so that it moves (thereby keeping the
* original position more or less centered).
......@@ -1543,6 +1590,118 @@ public class NodeSelect extends JApplet {
repaint();
}
}
/*
* This class and code comes from one of the examples on the Sun
* Java site.
*/
public class Ruler extends JComponent {
private int SIZE = 30;
private int orientation;
private int increment;
private int units;
public Ruler(int or) {
orientation = or;
// Tick every meter at any scale.
increment = (int) (pixels_per_meter * scale);
units = increment * 5;
}
public void reset () {
setsizes();
// Tick every meter at any scale.
increment = (int) (pixels_per_meter * scale);
units = increment * 5;
}
private void setsizes() {
int width;
int height;
if (orientation == HORIZONTAL) {
width = PhysMapPanel.getWidth();
height = SIZE;
}
else {
width = SIZE;
height = PhysMapPanel.getHeight();
}
System.out.println(orientation + ": " + width + "," + height);
setPreferredSize(new Dimension(width, height));
}
public int getIncrement() {
return increment;
}
public void setPreferredHeight(int ph) {
setPreferredSize(new Dimension(SIZE, ph));
}
public void setPreferredWidth(int pw) {
setPreferredSize(new Dimension(pw, SIZE));
}
protected void paintComponent(Graphics g) {
Rectangle drawHere = g.getClipBounds();
//System.out.println(drawHere);
// Fill clipping area with dirty brown/orange.
g.setColor(new Color(230, 163, 4));
g.fillRect(drawHere.x, drawHere.y,
drawHere.width, drawHere.height);
// Do the ruler labels in a small font that's black.
g.setFont(RulerFont);
g.setColor(Color.black);
// Some vars we need.
int end = 0;
int start = 0;
int tickLength = 0;
String label = null;
// Use clipping bounds to calculate first and last locations.
if (orientation == HORIZONTAL) {
start = (drawHere.x / increment) * increment;
end = (((drawHere.x + drawHere.width) / increment) + 1)
* increment;
}
else {
start = (drawHere.y / increment) * increment;
end = (((drawHere.y + drawHere.height) / increment) + 1)
* increment;
}
//System.out.println("Rulers Painting(" + orientation + "): " +
// start + "," + end);
// Draw ticks
for (int i = start; i < end; i += increment) {
if (i != 0 && i % units == 0) {
tickLength = 10;
label = Integer.toString(i/increment);
}
else {
tickLength = 7;
label = null;
}
if (tickLength != 0) {
if (orientation == HORIZONTAL) {
g.drawLine(i, SIZE-1, i, SIZE-tickLength-1);
if (label != null)
g.drawString(label, i-4, 15);
}
else {
g.drawLine(SIZE-1, i, SIZE-tickLength-1, i);
if (label != null)
g.drawString(label, 2, i+5);
}
}
}
}
}
}
/*
......
......@@ -334,13 +334,13 @@ elsif (@ARGV == 1) {
my $path_col = ($scale_arg == 0 ? "thumb_path" : "image_path");
my $db_scale = max($scale_arg, 1);
# Nozoom signals a call by robotmap. Don't use robots "building" otherwise.
my $robo_bldg = "b.building ". ($nozoom ? "" : "not "). "like '%ROBOTS%'";
my $robo_bldg = ($nozoom ? "" : "b.building not like '%ROBOTS%' and ");
my $query_result =
DBQueryFatal("select b.building,b.title,f.floor,f.$path_col,".
"f.pixels_per_meter ".
" from buildings as b ".
"left join floorimages as f on f.building=b.building ".
"where $robo_bldg and f.scale=$db_scale");
"where $robo_bldg f.scale=$db_scale");
if (!$query_result->numrows) {
die("*** $0:\n".
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -126,13 +126,13 @@ if (!(defined $min_x)) {
# adjust each node's position so topleftmost node is at (60,60) * $zoom.
foreach $i (keys %nodes) {
$nodes{$i}{"x"} = ($nodes{$i}{"x"} - $min_x + 60) * $zoom;
$nodes{$i}{"y"} = ($nodes{$i}{"y"} - $min_y + 60) * $zoom;
$nodes{$i}{"x"} = (($nodes{$i}{"x"} - $min_x) * $zoom) + 60;
$nodes{$i}{"y"} = (($nodes{$i}{"y"} - $min_y) * $zoom) + 60;
}
# adjust max x,y appropriately.
$max_x = ($max_x - $min_x + 120) * $zoom;
$max_y = ($max_y - $min_y + 120) * $zoom;
$max_x = (($max_x - $min_x) * $zoom) + 120;
$max_y = (($max_y - $min_y) * $zoom) + 120;
}
# get vlan list.
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# Copyright (c) 2004, 2005 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
......@@ -440,6 +440,9 @@ echo " Click on the dots below to see information about the node.\n";
echo " <br>\n";
echo " Click elsewhere on the map to set the center point for a zoomed-in view.\n";
echo " <br>\n";
echo " Check out the nifty new <a href=robotrack/selector.php3?building=MEB>";
echo "Java Applet</a> for selecting wireless nodes\n";
echo " <br>\n";
# Couldn't get JavaScript submit on checkbox to be portable to IE with map in form.
# Just use image buttons to show and invert the checkbox state. That works.
if ($ghost) {
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
chdir("..");
include("defs.php3");
#
# Only known and logged in users.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
#
# Verify page arguments. Allow user to optionally specify building/floor.
#
if (isset($building) && $building != "") {
# Sanitize for the shell.
if (!preg_match("/^[-\w]+$/", $building)) {
PAGEARGERROR("Invalid building argument.");
}
}
else {
PAGEARGERROR("Must supply a building.");
}
if (isset($floor) && $floor != "") {
if (!preg_match("/^[-\w]+$/", $floor)) {
PAGEARGERROR("Invalid floor argument.");
}
}
else {
PAGEARGERROR("Must supply a floor.");
}
#
# Make sure it exists in the DB.
#
$query_result =
DBQueryFatal("select pixels_per_meter from floorimages ".
"where building='$building' and floor='$floor'");
if (!mysql_num_rows($query_result)) {
PAGEARGERROR("No such building/floor $building/$floor");
}
#
# Need cleanup "handler" to make sure temp files get deleted!
#
function CLEANUP()
{
global $prefix, $uid;
#
# The backend script (vis/floormap.in) removes all the temp files
# with the -c option. Yucky, but file perms and owners make this
# the easiest way to do it.
#
if (isset($prefix)) {
SUEXEC($uid, "nobody", "webfloormap -o $prefix -k ");
# This file does belong to the web server.
unlink($prefix);
}
exit();
}
register_shutdown_function("CLEANUP");
#
# Create a tempfile to use as a unique prefix; it is not actually used but
# serves the same purpose (The script uses ${prefix}.jpg and ${prefix}.map .)
#
$prefix = tempnam("/tmp", "floormap");
#
# Build the image.
#
$perl_args = "-o $prefix -t -z -n -x -v -y -f $floor $building";
$retval = SUEXEC($uid, "nobody", "webfloormap $perl_args",
SUEXEC_ACTION_IGNORE);
if ($retval) {
SUEXECERROR(SUEXEC_ACTION_USERERROR);
# Never returns.
die("");
}
#
# Now spit it back.
#
if (($fp = fopen("${prefix}.jpg", "r"))) {
header("Content-type: image/jpg");
fpassthru($fp);
}
else {
# No Data. Spit back a stub image.
header("Content-type: image/gif");
readfile("coming-soon-thumb.gif");
}
?>
......@@ -72,8 +72,18 @@ while ($row = mysql_fetch_array($query_result)) {
$or = $row["orientation"];
$mobile = ($class == "robot" ? 1 : 0);
# In meters.
$size = ($class == "robot" ? 0.27 : 0.07);
$radius = ($class == "robot" ? 0.18 : 0.04);
if ($class == "robot") {
$size = 0.27;
$radius = 0.18;
}
elseif ($class == "pc" || $class == "pcwireless") {
$size = 1.0;
$radius = 0.5;
}
else {
$size = 0.07;
$radius = 0.04;
}
$alloc = (isset($pid) ? 1 : 0);
if (!isset($vname))
......
......@@ -21,52 +21,59 @@ if (isset($building) && $building != "") {
PAGEARGERROR("Invalid building argument.");
}
# Optional floor argument. Sanitize for the shell.
if (isset($floor) && !preg_match("/^[-\w]+$/", $floor)) {
PAGEARGERROR("Invalid floor argument.");
if (isset($floor) && $floor != "") {
if (!preg_match("/^[-\w]+$/", $floor)) {
PAGEARGERROR("Invalid floor argument.");
}
}
else
unset($floor);
}
else {
$building = "MEB-ROBOTS";
$floor = 4;
}
if (!isset($pid) ||
strcmp($pid, "") == 0) {
PAGEARGERROR("You must provide a Project ID.");
}
if (!isset($eid) ||
strcmp($eid, "") == 0) {
PAGEARGERROR("You must provide an Experiment ID.");
}
if (!preg_match("/^[-\w]+$/", $pid)) {
PAGEARGERROR("Invalid pid argument.");
}
if (!preg_match("/^[-\w]+$/", $eid)) {
PAGEARGERROR("Invalid eid argument.");
if (isset($pid) && $pid != "" && isset($eid) && $eid != "") {
if (!preg_match("/^[-\w]+$/", $pid)) {
PAGEARGERROR("Invalid pid argument.");
}
if (!preg_match("/^[-\w]+$/", $eid)) {
PAGEARGERROR("Invalid eid argument.");
}
#
# Check to make sure this is a valid PID/EID tuple.
#
if (! TBValidExperiment($pid, $eid)) {
USERERROR("Experiment $pid/$eid is not a valid experiment.", 1);
}
}
#
# Check to make sure this is a valid PID/EID tuple.
#
if (! TBValidExperiment($pid, $eid)) {
USERERROR("Experiment $pid/$eid is not a valid experiment.", 1);
else {
unset($pid);
unset($eid);
}
$ppm = 1;
#
# Grab pixel_per_meters for above map.
# Grab map info. Might be more then a single floor of course.
#
$query_result =
DBQueryFatal("select pixels_per_meter from floorimages ".
"where building='$building' and floor='$floor'");
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_array($query_result);
$ppm = $row["pixels_per_meter"];
if (isset($floor)) {
$query_result =
DBQueryFatal("select floor,pixels_per_meter from floorimages ".
"where building='$building' and scale=1 and ".
"floor='$floor'");
}
else {
USERERROR("No such building/floor $building/$floor", 1);
$query_result =
DBQueryFatal("select distinct fl.floor,fl.pixels_per_meter ".
" from location_info as loc ".
"left join floorimages as fl on ".
" loc.building=fl.building and loc.floor=fl.floor ".
"where fl.building='$building' and fl.scale=1 ".
"order by floor");
}
if (! mysql_num_rows($query_result)) {
USERERROR("No such building/floor", 1);
}
#
......@@ -78,83 +85,94 @@ echo "<table cellspacing=5 cellpadding=5 border=0 class=\"stealth\">
<table>
<tr><th colspan=2>Legend</th></tr>
<tr>
<td><img src='/autostatus-icons/redball.gif' alt=down></td>
<td nowrap=1>Dead Node</td>
<td><img src='/autostatus-icons/redball.gif' alt=allocated></td>
<td nowrap=1>Unavailable Node</td>
</tr>
<tr>
<td><img src='/autostatus-icons/greenball.gif' alt=down></td>
<td><img src='/autostatus-icons/greenball.gif'
alt=unassigned></td>
<td nowrap=1>Unassigned Node</td>
</tr>
<tr>
<td><img src='/autostatus-icons/yellowball.gif' alt=down></td>
<td><img src='/autostatus-icons/blueball.gif' alt=assigned></td>
<td nowrap=1>Assigned Node</td>
</tr>
<tr>
<td><img src='/autostatus-icons/yellowball.gif' alt=selected></td>
<td nowrap=1>Selected Node</td>
</tr>
</table>
</td>
<td class=stealth>This applet allows you to assign your virual nodes
to physical nodes. See below for instructions.
<td class=stealth>This applet allows you to select physical nodes
for your experiment. See below for instructions.
</td>
</tr>
</table><hr>\n";
#
# Create a tempfile to use as a unique prefix; it is not actually used but
# serves the same purpose (The script uses ${prefix}.jpg and ${prefix}.map .)
#
$prefix = tempnam("/tmp", "floormap");
#
# Get the unique part to send back.
#
if (!preg_match("/^\/tmp\/([-\w]+)$/", $prefix, $matches)) {
TBERROR("Bad tempnam: $prefix", 1);
}
$uniqueid = $matches[1];
$perl_args = "-o $prefix -t -z -n -x -v -y -f $floor $building";
$retval = SUEXEC($uid, "nobody", "webfloormap $perl_args",
SUEXEC_ACTION_IGNORE);
if ($retval) {
SUEXECERROR(SUEXEC_ACTION_USERERROR);
# Never returns.
die("");
}
$auth = $HTTP_COOKIE_VARS[$TBAUTHCOOKIE];
$baseurl = "../floormap_aux.php3?prefix=$uniqueid";
$auth = $HTTP_COOKIE_VARS[$TBAUTHCOOKIE];
$floorcount = mysql_num_rows($query_result);
$ppm = 1;
$index = 0;
echo "<applet name='selector' code='NodeSelect.class'
archive='selector.jar'
width='1025' height='1050'
archive='NodeSelect.jar'
width='1025' height='700'
alt='You need java to run this applet'>
<param name='floorurl' value='$baseurl'>
<param name='uid' value='$uid'>
<param name='auth' value='$auth'>
<param name='ppm' value='$ppm'>
<param name='building' value='$building'>
<param name='floor' value='$floor'>
<param name='pid' value='$pid'>
<param name='eid' value='$eid'>
</applet>\n";
<param name='building' value='$building'>\n";
echo " <param name='floorcount' value='$floorcount'>";
while (($row = mysql_fetch_array($query_result))) {
$floor = $row['floor'];
$ppm = $row['pixels_per_meter'];
echo " <param name='floor_${index}' value='$floor'>\n";
$index++;
}
echo " <param name='ppm' value='$ppm'>";
if (isset($pid)) {
echo " <param name='pid' value='$pid'>
<param name='eid' value='$eid'>\n";
}
echo "</applet>\n";
echo "<br>
<blockquote><blockquote>
<blockquote>
<center>
<h3>Using the Node Selector Applet</h3>
</center>
<ul>
<li> Left click over a node brings up a menu of virtual nodes. Select a
virtual node from the menu to assign to the physical node.
<li> Right click over a node brings up a menu that allows you to get
information about the physical node, or cancel the current
assignment.
<li> Left click over the background image brings up a menu to submit your
assignments.
<li> Nodes that are currently available for use are in the upper
list box.
<li> Nodes that have been selected for use (assigned) are in the
lower list box.
<li> Highlight nodes by selecting them in list boxes or clicking on them
in the maps.
Shift-Click does the usual thing; adds to an existing selection.
<li> Right click over a node will bring up a node context menu.
<li> Right click anyplace else brings up the root context menu.
<li> Move highlighted nodes between the upper and lower lists by choosing
the appropriate option in either context menu.
<li> Once you have your nodes assigned (the lower list box), use the
'Create NS File' to menu option to popup an NS fragment that you
can plug into an existing experiment.
</ul>
Notes:<br>
<ul>
<li> Middle click repositions the map so that the point under
the mouse moves to the center.
<li> Left click and drag in the map scrolls the map around.
<li> Zoom in and out using the options on the Root Context Menu.
<li> Return the map to the most recent centering position with the
<em>Recenter</em> option.
<li> Reset the zoom and center to initial startup conditions with
the <em>Reset</em> option.
<li> The 'rulers' are in 1 meter increments and are intended to provide
scale information; they are not relative to a specific origin.
</ul>
<blockquote><blockquote>\n";
</blockquote>\n";
PAGEFOOTER();
?>
......@@ -68,18 +68,22 @@ register_shutdown_function("SPEWCLEANUP");
# Get the virtual node info
$query_result =
DBQueryFatal("select vname,fixed from virt_nodes ".
"where pid='$pid' and eid='$eid' ".
"order by vname");
DBQueryFatal("select v.vname,fixed,vis.x,vis.y from virt_nodes as v ".
"left join vis_nodes as vis on ".
" vis.pid=v.pid and vis.eid=v.eid and vis.vname=v.vname ".
"where v.pid='$pid' and v.eid='$eid' ".
"order by v.vname");
while ($row = mysql_fetch_array($query_result)) {
$vname = $row["vname"];
$fixed = $row["fixed"];
$x = (int) $row["x"];
$y = (int) $row["y"];