/*
* Copyright 2014-2016 Cel Skeggs.
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE 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.
*
* The CCRE 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 the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.supercanvas.components.palette;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.TreeSet;
import ccre.channel.BooleanInput;
import ccre.channel.BooleanOutput;
import ccre.channel.EventInput;
import ccre.channel.EventOutput;
import ccre.channel.FloatInput;
import ccre.channel.FloatOutput;
import ccre.cluck.Cluck;
import ccre.cluck.CluckConstants;
import ccre.cluck.CluckPublisher;
import ccre.cluck.CluckRemoteListener;
import ccre.cluck.rpc.RemoteProcedure;
import ccre.log.Logger;
import ccre.log.LoggingTarget;
import ccre.rconf.RConfable;
import ccre.supercanvas.SuperCanvasComponent;
import ccre.supercanvas.components.channels.BooleanControlComponent;
import ccre.supercanvas.components.channels.BooleanDisplayComponent;
import ccre.supercanvas.components.channels.EventControlComponent;
import ccre.supercanvas.components.channels.EventDisplayComponent;
import ccre.supercanvas.components.channels.FloatControlComponent;
import ccre.supercanvas.components.channels.FloatDisplayComponent;
import ccre.supercanvas.components.channels.LoggingTargetControlComponent;
import ccre.supercanvas.components.channels.OutputStreamControlComponent;
import ccre.supercanvas.components.channels.RConfComponent;
import ccre.supercanvas.components.channels.RPCControlComponent;
import ccre.timers.PauseTimer;
import ccre.util.UniqueIds;
/**
* A palette that contains all the visible objects on the network.
*
* @author skeggsc
*/
public class NetworkPaletteComponent extends PaletteComponent<Collection<NetworkPaletteElement>> {
/**
* Fake RMT number used for merged event channels.
*/
public static final int F_RMT_EVENTS = -1;
/**
* Fake RMT number used for merged float channels.
*/
public static final int F_RMT_FLOATS = -2;
/**
* Fake RMT number used for merged boolean channels.
*/
public static final int F_RMT_BOOLEANS = -3;
/**
* Fake RMT number used for detected rconf endpoints.
*/
public static final int F_RMT_RCONF = -4;
private static final long serialVersionUID = -2162354007005983283L;
static SuperCanvasComponent createComponent(String name, Object target, int type, int x, int y) {
switch (type) {
case CluckConstants.RMT_BOOLOUTP:
return new BooleanControlComponent(x, y, name, (BooleanOutput) target);
case CluckConstants.RMT_BOOLINPUT:
return new BooleanDisplayComponent(x, y, name, (BooleanInput) target);
case CluckConstants.RMT_EVENTOUTP:
return new EventControlComponent(x, y, name, (EventOutput) target);
case CluckConstants.RMT_EVENTINPUT:
return new EventDisplayComponent(x, y, name, (EventInput) target);
case CluckConstants.RMT_FLOATOUTP:
return new FloatControlComponent(x, y, name, (FloatOutput) target);
case CluckConstants.RMT_FLOATINPUT:
return new FloatDisplayComponent(x, y, name, (FloatInput) target);
case F_RMT_EVENTS:
return new EventControlComponent(x, y, name, (EventInput) ((Object[]) target)[0], (EventOutput) ((Object[]) target)[1]);
case F_RMT_FLOATS:
return new FloatControlComponent(x, y, name, (FloatInput) ((Object[]) target)[0], (FloatOutput) ((Object[]) target)[1]);
case F_RMT_BOOLEANS:
return new BooleanControlComponent(x, y, name, (BooleanInput) ((Object[]) target)[0], (BooleanOutput) ((Object[]) target)[1]);
case F_RMT_RCONF:
return new RConfComponent(x, y, name, (RConfable) target);
case CluckConstants.RMT_OUTSTREAM:
return new OutputStreamControlComponent(x, y, name, (OutputStream) target);
case CluckConstants.RMT_LOGTARGET:
return new LoggingTargetControlComponent(x, y, name, (LoggingTarget) target);
case CluckConstants.RMT_INVOKE:
return new RPCControlComponent(x, y, name, (RemoteProcedure) target);
default:
Logger.warning("Could not display RMT of " + CluckConstants.rmtToString(type));
return null;
}
}
private static Object subscribeByType(String path, int type) {
switch (type) {
case CluckConstants.RMT_BOOLOUTP:
return Cluck.subscribeBO(path);
case CluckConstants.RMT_BOOLINPUT:
return Cluck.subscribeBI(path, false);
case CluckConstants.RMT_EVENTOUTP:
return Cluck.subscribeEO(path);
case CluckConstants.RMT_EVENTINPUT:
return Cluck.subscribeEI(path);
case CluckConstants.RMT_FLOATOUTP:
return Cluck.subscribeFO(path);
case CluckConstants.RMT_FLOATINPUT:
return Cluck.subscribeFI(path, false);
case CluckConstants.RMT_INVOKE:
// Is this a good amount of time?
return Cluck.getNode().getRPCManager().subscribe(path, 500);
case CluckConstants.RMT_LOGTARGET:
return Cluck.subscribeLT(path);
case CluckConstants.RMT_OUTSTREAM:
return Cluck.subscribeOS(path);
default:
Logger.warning("Could not subscribe to RMT of " + CluckConstants.rmtToString(type));
return null;
}
}
private transient PauseTimer researcher;
/**
* Create a new NetworkPaletteComponent at the specified position.
*
* @param cx The X coordinate.
* @param cy The Y coordinate.
*/
public NetworkPaletteComponent(int cx, int cy) {
super(cx, cy, Collections.synchronizedSet(new TreeSet<NetworkPaletteElement>()));
start();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
start();
}
@Override
protected boolean onInteractWithTitleBar() {
researcher.event();
return true;
}
private void start() {
this.researcher = new PauseTimer(500);
final EventOutput searcher = CluckPublisher.setupSearching(Cluck.getNode(), new CluckRemoteListener() {
@Override
public synchronized void handle(String remote, int remoteType) {
for (NetworkPaletteElement e : entries) {
if (e.getName().equals(remote)) {
if (e.getType() != remoteType) {
Logger.warning("Mismatched remote type in search!");
}
return;
}
}
Object sub = subscribeByType(remote, remoteType);
if (sub != null) {
entries.add(new NetworkPaletteElement(remote, sub, remoteType));
}
String pairName, base;
int expect, type;
boolean isExpectInput;
if (remote.endsWith(".input")) {
isExpectInput = false;
base = remote.substring(0, remote.length() - 6);
pairName = base + ".output";
switch (remoteType) {
case CluckConstants.RMT_BOOLINPUT:
expect = CluckConstants.RMT_BOOLOUTP;
type = F_RMT_BOOLEANS;
break;
case CluckConstants.RMT_FLOATINPUT:
expect = CluckConstants.RMT_FLOATOUTP;
type = F_RMT_FLOATS;
break;
case CluckConstants.RMT_EVENTINPUT:
expect = CluckConstants.RMT_EVENTOUTP;
type = F_RMT_EVENTS;
break;
default:
return;
}
} else if (remote.endsWith(".output")) {
isExpectInput = true;
base = remote.substring(0, remote.length() - 7);
pairName = base + ".input";
switch (remoteType) {
case CluckConstants.RMT_BOOLOUTP:
expect = CluckConstants.RMT_BOOLINPUT;
type = F_RMT_BOOLEANS;
break;
case CluckConstants.RMT_FLOATOUTP:
expect = CluckConstants.RMT_FLOATINPUT;
type = F_RMT_FLOATS;
break;
case CluckConstants.RMT_EVENTOUTP:
expect = CluckConstants.RMT_EVENTINPUT;
type = F_RMT_EVENTS;
break;
default:
return;
}
} else {
if ((remote.endsWith("-rpcq") || remote.endsWith("-rpcs")) && remoteType == CluckConstants.RMT_INVOKE) {
base = remote.substring(0, remote.length() - 5);
pairName = base + (remote.endsWith("s") ? "-rpcq" : "-rpcs");
for (NetworkPaletteElement e : entries) {
if (pairName.equals(e.getName()) && e.type == CluckConstants.RMT_INVOKE) {
entries.add(new NetworkPaletteElement(base, Cluck.subscribeRConf(base, 500), F_RMT_RCONF));
break;
}
}
}
return;
}
Object pair = null;
for (NetworkPaletteElement e : entries) {
if (pairName.equals(e.getName()) && e.type == expect) {
pair = e.target;
}
}
if (pair != null) {
Object[] send = isExpectInput ? new Object[] { pair, sub } : new Object[] { sub, pair };
entries.add(new NetworkPaletteElement(base, send, type));
}
}
});
researcher.triggerAtEnd(() -> {
entries.clear();
searcher.event();
});
Cluck.getNode().subscribeToStructureNotifications(UniqueIds.global.nextHexId("notification-subscriber"), researcher);
searcher.safeEvent();
}
/**
* Try to find the named Cluck reference and create a new component at the
* given position, if found.
*
* @param x the X-position of the new component.
* @param y the Y-position of the new component.
* @param ref the Cluck reference to search for.
* @return the new SuperCanvasComponent, or null if the reference cannot be
* found.
*/
public SuperCanvasComponent getComponentFor(int x, int y, String ref) {
for (NetworkPaletteElement elem : entries) {
if (ref.equals(elem.getName())) {
return elem.fetch(x, y);
}
}
return null;
}
@Override
public boolean onReceiveDrop(int x, int y, final SuperCanvasComponent activeComponent) {
if (activeComponent.onDelete(false)) {
getPanel().remove(activeComponent);
}
return true;
}
}