Fixed networking performance in OPC server
[SugarCubes.git] / OpenPixelControl.pde
CommitLineData
3dcfa14e
MES
1
2import java.io.*;
3import java.net.*;
3ffd6241
MES
4
5// Server for Open Pixel Control patterns (http://openpixelcontrol.org/)
3dcfa14e 6class OpenPixelControl extends SCPattern implements Runnable {
3ffd6241
MES
7
8 int port = 7890;
3dcfa14e
MES
9 Thread thread;
10 ServerSocket server;
3ffd6241
MES
11 byte[] buffer;
12 int bufferedByteCount;
13
14 public OpenPixelControl(LX lx, PApplet parent) {
15 super(lx);
3dcfa14e
MES
16 parent.registerMethod("dispose", this);
17
18 // Save a JSON layout file that some Open Pixel Control clients can use
19 writeMappingFile("openpixelcontrol-layout.json");
3ffd6241
MES
20
21 // Buffer space for two frames, worst case
22 buffer = new byte[0x10004 * 2];
23 bufferedByteCount = 0;
3ffd6241
MES
24 }
25
3dcfa14e
MES
26 public void dispose() {
27 thread = null;
28 if (server != null) {
29 try {
30 server.close();
31 } catch (IOException e) {
32 e.printStackTrace();
33 }
34 server = null;
35 }
3ffd6241
MES
36 }
37
3dcfa14e
MES
38 public void run() {
39 // Thread run function; handle OPC traffic.
40 while (Thread.currentThread() == thread) {
41
42 try {
43 Socket cli = server.accept();
44 InputStream input = cli.getInputStream();
45 bufferedByteCount = 0;
46
47 while (Thread.currentThread() == thread) {
48
49 int r = input.read(buffer, bufferedByteCount, buffer.length - bufferedByteCount);
50 if (r <= 0) {
51 break;
52 }
53 bufferedByteCount += r;
54
55 // Extract OPC packets from buffer
56 int offset = 0;
57 while (bufferedByteCount - offset >= 4) {
58 int channel = buffer[offset + 0] & 0xFF;
59 int command = buffer[offset + 1] & 0xFF;
60 int length = ((buffer[offset + 2] & 0xFF) << 8) | (buffer[offset + 3] & 0xFF);
61
62 if (bufferedByteCount - offset < length + 4) {
63 // Not enough data for a full packet yet
64 break;
65 }
66
67 // Handle the packet in-place
68 offset += 4;
69 opcPacket(channel, command, offset, length);
70 offset += length;
71 }
72
73 // If we didn't use the whole buffer, save remainder for later
74 bufferedByteCount -= offset;
75 arrayCopy(buffer, offset, buffer, 0, bufferedByteCount);
76 }
3ffd6241 77
3dcfa14e 78 cli.close();
3ffd6241 79
3dcfa14e
MES
80 } catch (IOException e) {
81 e.printStackTrace();
3ffd6241 82 }
3dcfa14e
MES
83 }
84 }
3ffd6241 85
3dcfa14e
MES
86 public void run(double deltaMs) {
87 // SCPattern run function; nothing to do except start our thread the first time.
88
89 if (server == null) {
90 try {
91 server = new ServerSocket(this.port);
92 thread = new Thread(this);
93 thread.start();
94 println("Listening for Open Pixel Control data on port " + port);
95
96 } catch (IOException e) {
97 e.printStackTrace();
98 thread = null;
3ffd6241 99 }
3ffd6241
MES
100 }
101 }
102
103 void opcPacket(int channel, int command, int offset, int length) {
104 // Only look at "Set Pixel Colors" to channel 0.
105 if (channel == 0 && command == 0) {
106
107 // Unpack colors directly into framebuffer
108 for (int i = 0; i < length / 3; i++) {
109 if (i >= colors.length) {
110 break;
111 }
112
113 colors[i] =
114 ((buffer[offset + 0] & 0xFF) << 16) |
115 ((buffer[offset + 1] & 0xFF) << 8) |
116 (buffer[offset + 2] & 0xFF) ;
117
118 offset += 3;
119 }
120 }
121 }
122
123 void writeMappingFile(String filename) {
124 PrintWriter output;
125 output = createWriter(filename);
126
127 // Rearrange points by color buffer index
128 LXPoint[] orderedPoints;
129 int maxIndex = 0;
130 for (LXPoint p : model.points) {
131 maxIndex = max(maxIndex, p.index);
132 }
133 orderedPoints = new LXPoint[maxIndex + 1];
134 for (LXPoint p : model.points) {
135 orderedPoints[p.index] = p;
136 }
137
138 float xCenter = (model.xMax + model.xMin) / 2.0;
139 float yCenter = (model.yMax + model.yMin) / 2.0;
140 float zCenter = (model.zMax + model.zMin) / 2.0;
141 float xSize = model.xMax - model.xMin;
142 float zSize = model.zMax - model.zMin;
143 float maxSize = max(xSize, zSize);
144 float scale = 4.0 / maxSize;
145
146 output.print("[");
147 for (int i = 0; i < orderedPoints.length; i++) {
148 boolean isLast = i == orderedPoints.length - 1;
149 String comma = isLast ? "" : ",";
150 LXPoint p = orderedPoints[i];
151
152 if (p == null) {
153 // Unused index
154 output.print("null" + comma);
155 } else {
156 // Transform coordinates to make sense in the OPC visualizer
157 float x = (p.x - xCenter) * scale;
158 float y = (p.z - zCenter) * scale;
159 float z = (p.y - yCenter) * scale;
160
161 output.print("{\"point\":[" + x + "," + y + "," + z + "]}" + comma);
162 }
163 }
164 output.print("]");
165
166 output.flush();
167 output.close();
168 println("Saved OpenPixelControl model to " + filename);
169 }
170};