import netP5.*;
import oscP5.*;
-//import hypermedia.net.*;
-
-
-
/**
* DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
* This class implements the output function to the Panda Boards. It
* will be moved into GLucose once stabilized.
*/
-public static class PandaDriver extends Thread{
- int lastSeen;
- void start(){super.start();}
- void run(){
- while(true){
- if(queue.size()>0) {
- for(int i=0; i<queue.size(); i++){
- this.sendNow(queue.get(queue.size()-1));
- queue.clear();
- }
- } else {
- try{sleep(1);} catch(Exception e){}
- }
- }
+public static class PandaDriver {
+
+ interface Listener {
+ public void onToggle(boolean enabled);
}
-
- //UDP udp; // define the UDP object
-
+
+ private Listener listener = null;
+
// IP address
public final String ip;
+
+ public PandaMapping pm;
+
+ private final static int PORT = 779;
+
+ private final DatagramSocket socket;
// Address to send to
private final NetAddress address;
// Whether board output is enabled
private boolean enabled = false;
+
+ // Frame count for Grizzlies
+ private int frameNum = 1;
// OSC message
private final OscMessage message;
private final int[] points;
// Packet data
- private final byte[] packet = new byte[4*352]; // magic number, our UDP packet size
+ private final byte[] packet = new byte[4*280]; // magic number, our UDP packet size
private static final int NO_POINT = -1;
- public PandaDriver(String ip) {
- this.ip = ip;
-
- //udp = new UDP(this, 10000);
-
- // Initialize our OSC output stuff
- address = new NetAddress(ip, 9001);
- message = new OscMessage("/shady/pointbuffer");
-
- // Build the array of points, initialize all to nothing
- points = new int[PandaMapping.PIXELS_PER_BOARD];
- for (int i = 0; i < points.length; ++i) {
- points[i] = NO_POINT;
- }
- this.start();
- }
-
- private final static int FORWARD = -1;
- private final static int BACKWARD = -2;
+ private Model _model;
////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////
+ private final static int FORWARD = -1;
+ private final static int BACKWARD = -2;
/**
* These constant arrays indicate the order in which the strips of a cube
* corner of the cube the data wire comes in.
*/
private final static int[][] CUBE_STRIP_ORDERINGS = new int[][] {
+// { 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 }, // FRONT_LEFT
+// { 6, 5, 4, 7, 1, 0, 3, 2, 8, 11, 10, 9, 15, 14, 13, 12 }, // FRONT_RIGHT
+// { 14, 13, 12, 15, 9, 8, 11, 10, 0, 3, 2, 1, 7, 6, 5, 4 }, // REAR_LEFT
+// { 10, 9, 8, 11, 5, 4, 7, 6, 12, 15, 14, 13, 3, 2, 1, 0 }, // REAR_RIGHT
+
+
{ 2, 1, 0, 3, 13, 12, 15, 14, 4, 7, 6, 5, 11, 10, 9, 8 }, // FRONT_LEFT
{ 6, 5, 4, 7, 1, 0, 3, 2, 8, 11, 10, 9, 15, 14, 13, 12 }, // FRONT_RIGHT
{ 14, 13, 12, 15, 9, 8, 11, 10, 0, 3, 2, 1, 7, 6, 5, 4 }, // REAR_LEFT
- { 10, 9, 8, 11, 5, 4, 7, 6, 12, 15, 14, 13, 3, 2, 1, 0 }, // REAR_RIGHT
+ { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 }, // REAR_RIGHT
+
};
private final static int[][] BASS_STRIP_ORDERING = {
// as cubes with Wiring.FRONT_LEFT. If this needs to be changed,
// remove this null assignment and change the below to have mappings
// for the LEFT and RIGHT speaker
- private final static int[][][] SPEAKER_STRIP_ORDERING = null; /* {
+ private final static int[][][] SPEAKER_STRIP_ORDERING = {
// Left speaker
{
// Front face, counter-clockwise from bottom left
{0, BACKWARD },
{3, BACKWARD },
}
- };*/
-
- private final static int[][] LEFT_SPEAKER_STRIP_ORDERING = {
};
- ArrayList<int[]> queue;
-
- public PandaDriver(String ip, Model model, PandaMapping pm) {
- this(ip);
+ public PandaDriver(String ip) {
+ this.ip = ip;
- queue = new ArrayList<int[]>();
-
+ // Initialize our OSC output stuff
+ address = new NetAddress(ip, 779);
+ message = new OscMessage("/shady/pointbuffer");
+
+ try {
+ socket = new DatagramSocket();
+ } catch (Exception x) {
+ throw new RuntimeException(x);
+ }
+
+ // Build the array of points, initialize all to nothing
+ points = new int[PandaMapping.PIXELS_PER_BOARD];
+ for (int i = 0; i < points.length; ++i) {
+ points[i] = NO_POINT;
+ }
+ }
+
+ public PandaDriver(String ip, Model model, PandaMapping _pm) {
+ this(ip);
+ pm = _pm;
+ _model = model;
// Ok, we are initialized, time to build the array if points in order to
// send out. We start at the head of our point buffer, and work our way
// down. This is the order in which points will be sent down the wire.
int ci = -1;
- // Iterate through all our channels
+ // Iterate through all our channelq s
for (ChannelMapping channel : pm.channelList) {
++ci;
int pi = ci * ChannelMapping.PIXELS_PER_CHANNEL;
case REAR_RIGHT: stripOrderIndex = 3; break;
}
+ // TODO(mcslee): clean up, ordering always consistent now
+ stripOrderIndex = 2;
+
// Iterate through all the strips on the cube and add the points
for (int stripIndex : CUBE_STRIP_ORDERINGS[stripOrderIndex]) {
// We go backwards here... in the model strips go clockwise, but
}
private int mapStrip(Strip s, int direction, int[] points, int pi) {
+ return mapStrip(s, direction, points, pi, s.points.size());
+ }
+
+ private int mapStrip(Strip s, int direction, int[] points, int pi, int len) {
if (direction == FORWARD) {
- for (Point p : s.points) {
+ int i = 0;
+ for (LXPoint p : s.points) {
points[pi++] = p.index;
+ if (++i >= len) {
+ break;
+ }
}
} else if (direction == BACKWARD) {
- for (int i = s.points.size()-1; i >= 0; --i) {
+ for (int i = len-1; i >= 0; --i) {
points[pi++] = s.points.get(i).index;
}
} else {
return pi;
}
- public void disable() {
- if (enabled) {
- enabled = false;
- println("PandaBoard/" + ip + ": OFF");
+ public PandaDriver setListener(Listener listener) {
+ this.listener = listener;
+ return this;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (this.enabled != enabled) {
+ this.enabled = enabled;
+ println("PandaBoard/" + ip + ": " + (enabled ? "ON" : "OFF"));
+ if (listener != null) {
+ listener.onToggle(enabled);
+ }
}
}
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void disable() {
+ setEnabled(false);
+ }
+
public void enable() {
- if (!enabled) {
- enabled = true;
- println("PandaBoard/" + ip + ": ON");
- }
+ setEnabled(true);
}
public void toggle() {
- enabled = !enabled;
- println("PandaBoard/" + ip + ": " + (enabled ? "ON" : "OFF"));
+ setEnabled(!enabled);
}
+ private final int[] GRIZZLY_STRIP_ORDERING = new int[] { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 };
+
public final void send(int[] colors) {
- queue.add(colors);
- }
- public final void sendNow(int[] colors) {
- if (!enabled || colors==null) {
+ if (!enabled) {
return;
}
+ frameNum++;
int len = 0;
int packetNum = 0;
- if(points==null) { return; }
- for (int index: points) {
- int c = 0;
- if(index>0) { c= colors[index]; }
- byte r = (byte) ((c >> 16) & 0xFF);
- byte g = (byte) ((c >> 8) & 0xFF);
- byte b = (byte) ((c) & 0xFF);
- packet[len++] = 0; // alpha channel, unused but makes for 4-byte alignment
- packet[len++] = r;
- packet[len++] = g;
- packet[len++] = b;
-
- // Flush once packet is full buffer size
- if (len >= packet.length) {
- sendPacket(packetNum++);
- len = 0;
+ for (ChannelMapping channel : pm.channelList) {
+ for (int rawCubeIndex : channel.objectIndices) {
+ if (rawCubeIndex > 0) {
+ Cube cube = _model.getCubeByRawIndex(rawCubeIndex);
+
+ // TODO(mcslee): clean this up, precompute paths
+ for (int stripIndex : GRIZZLY_STRIP_ORDERING) {
+ Strip strip = cube.strips.get(stripIndex);
+ int stripLen = ((stripIndex == 9) || (stripIndex == 16)) ? 15 : 16;
+ for (int i = stripLen-1; i >= 0; --i) {
+ int c = colors[strip.points.get(i).index];
+ byte r = (byte) ((c >> 16) & 0xFF);
+ byte g = (byte) ((c >> 8) & 0xFF);
+ byte b = (byte) ((c) & 0xFF);
+ packet[len++] = (byte) 0; // alpha channel, unused but makes for 4-byte alignment
+ packet[len++] = (byte) r;
+ packet[len++] = (byte) g;
+ packet[len++] = (byte) b;
+ }
+ }
+
+// for (LXPoint p : cube.points) {
+// int c = (p.index < 0) ? 0 : colors[p.index];
+// byte r = (byte) ((c >> 16) & 0xFF);
+// byte g = (byte) ((c >> 8) & 0xFF);
+// byte b = (byte) ((c) & 0xFF);
+// packet[len++] = (byte) 0; // alpha channel, unused but makes for 4-byte alignment
+// packet[len++] = (byte) r;
+// packet[len++] = (byte) g;
+// packet[len++] = (byte) b;
+// }
+ }
}
+ // println("Packet number: " + packetNum);
+ sendPacket(frameNum, packetNum++);
+ len = 0;
}
+ // for (int index : points) {
+ // int c = (index < 0) ? 0 : colors[index];
+ // byte r = (byte) ((c >> 16) & 0xFF);
+ // byte g = (byte) ((c >> 8) & 0xFF);
+ // byte b = (byte) ((c) & 0xFF);
+ // packet[len++] = 0; // alpha channel, unused but makes for 4-byte alignment
+ // packet[len++] = r;
+ // packet[len++] = g;
+ // packet[len++] = b;
- // Flush any remaining data
- if (len > 0) {
- sendPacket(packetNum++);
- }
+ // // Flush once packet is full buffer size
+ // if (len >= packet.length) {
+ // sendPacket(packetNum++);
+ // len = 0;
+ // }
+ // }
+
+ // // Flush any remaining data
+ // if (len > 0) {
+ // sendPacket(packetNum++);
+ // }
}
- private void sendPacket(int packetNum) {
+ private void sendPacket(int frameNum, int packetNum) {
+ // println("Sending frame #" + frameNum + ", channel # " + packetNum);
message.clearArguments();
+ message.add(frameNum);
+ message.add(0xDEADBEEF);
message.add(packetNum);
+ message.add(0xFEEDBEEF);
message.add(packet.length);
message.add(packet);
+ message.add(0xBEFFFFEB);
+
try {
- //udp.send(packet, "10.200.1.29", 9001);
- OscP5.flush(message, address);
+ // OscP5.flush(message, address); // new DatagramSocket every time, no thanks
+ byte[] bytes = message.getBytes();
+ DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address.inetaddress(), PORT);
+ socket.send(packet);
} catch (Exception x) {
x.printStackTrace();
}