-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathCortexOutput.pde
More file actions
280 lines (231 loc) · 8.22 KB
/
CortexOutput.pde
File metadata and controls
280 lines (231 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
* Motor cortex:
* Create output to the LEDs via OSC packets to BeagleBone Black
* Each BBB pushes up to 24 channels of <=512 pixels per channel
*
* @author mjp 2015.08.08
*/
import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import heronarts.lx.LX;
import java.util.*;
//private int mimsyChannelPixels = 123;
private int mimsyChannelPixels = 123;
private final String ControllerIPs[] = {
"192.168.1.85",
//"192.168.1.81",
//"192.168.1.87",
// "192.168.1.86",
};
//public ArrayList<int[]> channelMap;
/* ********** Physical Limits on Channels ********************************** */
int nPixPerChannel = 256; // OPC server is set to 512 pix per channel
int nChannelPerBoard = 5;
//int nChannelPerBoard = 48;
/* ********** Create an array for each board mapping each pixel to a channel */
int[] concatenateChannels(int boardNum) {
// expects boardNum to be indexed starting at *1*
System.out.format("Cortex #%d: %s", boardNum, ControllerIPs[boardNum-1]);
//println("concatenating board " + boardNum);
int[] pixIndex = new int[nPixPerChannel*nChannelPerBoard];
int boardOffset = (boardNum-1) * nChannelPerBoard;
int pixels = 0;
for (int i=boardOffset; i<boardOffset+nChannelPerBoard; i++) {
int[] channelIx = new int[mimsyChannelPixels];
for (int p = 0; p < mimsyChannelPixels; p++) {
channelIx[p] = pixels++;
}
//int[] channelIx = model.channelMap.get(i);
System.out.format(" -- adding channel %d: %5d pixels\n", i, channelIx.length);
for(int j=0; j<channelIx.length; j++) {
//System.out.format(" ---- Pixel %8d\n", i * nPixPerChannel - boardOffset*nPixPerChannel + j);
//println( i * nPixPerChannel - boardOffset*nPixPerChannel + j);
pixIndex[i * nPixPerChannel - boardOffset*nPixPerChannel + j] = channelIx[j];
}
}
return pixIndex;
}
/* ********** Final routine to set up the output ****************************/
void buildOutputs() {
//for (int i = 1; i < ControllerIPs.length; i++) {
// lx.addOutput(new CortexOutput(lx ,ControllerIPs[i-1], i, concatenateChannels(i)));
//}
lx.addOutput(new CortexOutput(lx ,"192.168.1.85", 1, concatenateChannels(1)));
//lx.addOutput(new CortexOutput(lx ,"192.168.1.81", 2, concatenateChannels(2)));
//lx.addOutput(new CortexOutput(lx, "192.168.1.87", 3, concatenateChannels(3)));
//lx.addOutput(new CortexOutput(lx ,"192.168.1.86", 3, concatenateChannels(3)));
}
/* ********** List of Output Boards for UI **********************************/
ArrayList<CortexOutput> cortexList = new ArrayList<CortexOutput>();
public class CortexOutput extends LXOutput {
// constants for creating OPC header
static final int HEADER_LEN = 4;
static final int BYTES_PER_PIXEL = 3;
static final int INDEX_CHANNEL = 0;
static final int INDEX_COMMAND = 1;
static final int INDEX_DATA_LEN_MSB = 2;
static final int INDEX_DATA_LEN_LSB = 3;
static final int INDEX_DATA = 4;
static final int OFFSET_R = 0;
static final int OFFSET_G = 1;
static final int OFFSET_B = 2;
static final int COMMAND_SET_PIXEL_COLORS = 0;
static final int PORT = 7890; //the standard OPC port
Socket socket;
OutputStream output;
String host;
int port = 7890;
public int boardNum;
public int channelNum;
public byte[] packetData;
private final int[] pointIndices;
CortexOutput(LX lx, String _host, int _boardNum, int[] _pointIndices) {
super(lx);
this.host = _host;
this.boardNum = _boardNum;
this.pointIndices = _pointIndices;
this.socket = null;
this.output = null;
enabled.setValue(true);
cortexList.add(this);
int dataLength = BYTES_PER_PIXEL*nPixPerChannel*nChannelPerBoard;
this.packetData = new byte[HEADER_LEN + dataLength];
this.packetData[INDEX_CHANNEL] = 0;
this.packetData[INDEX_COMMAND] = COMMAND_SET_PIXEL_COLORS;
this.packetData[INDEX_DATA_LEN_MSB] = (byte)(dataLength >>> 8);
this.packetData[INDEX_DATA_LEN_LSB] = (byte)(dataLength & 0xFF);
this.connect();
}
public boolean isConnected() {
return (this.output != null);
}
private void connect() {
// if (this.socket == null) {
if (this.output == null) {
try {https://docs.google.com/document/d/1dLYE5T73vIV1aaOHn3hhgKB0X57BxKOBDICkeJl_48o/edit#
this.socket = new Socket();
this.socket.connect(new InetSocketAddress(this.host, this.port), 100);
// this.socket.setTcpNoDelay(true); // disable on SugarCubes
this.output = this.socket.getOutputStream();
didConnect();
}
catch (ConnectException cx) {
dispose(cx);
}
catch (IOException iox) {
dispose(iox);
}
}
}
protected void didConnect() {
// println("Connected to OPC server: " + host + " for channel " + channelNum);
}
protected void closeChannel() {
try {
this.output.close();
this.socket.close();
}
catch (IOException e) {
println("tried closing a channel and fucked up");
}
}
protected void dispose() {
if (output != null) {
closeChannel();
}
this.socket = null;
this.output = null;
}
protected void dispose(Exception x) {
if (output != null) println("Disconnected from OPC server");
this.socket = null;
this.output = null;
didDispose(x);
}
protected void didDispose(Exception x) {
// println("Failed to connect to OPC server " + host);
// println("disposed");
}
// @Override
protected void onSend(int[] colors) {
if (packetData == null || packetData.length == 0) return;
float hsb[] = new float[3];
for(int i=0; i<colors.length; i++){
// TODO MJP: this might not work as expected, if we are dimming the global color array for each datagram that is sent
LXColor.RGBtoHSB(colors[i], hsb);
float b = hsb[2];
colors[i] = lx.hsb(360.*hsb[0], 100.*hsb[1], 100*(b*(float)global_brightness));
}
//connect();
if (isConnected()) {
try {
this.output.write(getPacketData(colors));
}
catch (IOException iox) {
dispose(iox);
}
}
}
// @Override
protected byte[] getPacketData(int[] colors) {
//System.out.format(" ---- Packet Pixels: %8d", colors.length);
for (int i = 0; i < this.pointIndices.length; ++i) {
int dataOffset = INDEX_DATA + i * BYTES_PER_PIXEL;
int pointIndex = this.pointIndices[i];
int c = colors[pointIndex];
this.packetData[dataOffset + OFFSET_R] = (byte) (0xFF & (c >> 16));
this.packetData[dataOffset + OFFSET_G] = (byte) (0xFF & (c >> 8));
this.packetData[dataOffset + OFFSET_B] = (byte) (0xFF & c);
}
// all other values in packetData should be 0 by default
return this.packetData;
}
}
//---------------------------------------------------------------------------------------------
// add UI components for the hardware, allowing enable/disable
class UIOutput extends UIWindow {
UIOutput(UI ui, float x, float y, float w, float h) {
super(ui, "OUTPUT", x, y, w, h);
float yPos = UIWindow.TITLE_LABEL_HEIGHT - 2;
List<UIItemList.Item> items = new ArrayList<UIItemList.Item>();
items.add(new OutputItem());
new UIItemList(1, yPos, width-2, 260)
.setItems(items)
.addToContainer(this);
}
class OutputItem extends UIItemList.AbstractItem {
OutputItem() {
for (CortexOutput ch : cortexList) {
ch.enabled.addListener(new LXParameterListener() {
public void onParameterChanged(LXParameter parameter) {
redraw();
}
}
);
}
}
String getLabel() {
return "ALL CHANNELS";
}
boolean isSelected() {
// jut check the first one, since they either should all be on or all be off
return cortexList.get(0).enabled.isOn();
}
void onMousePressed() {
for (CortexOutput ch : cortexList) {
ch.enabled.toggle();
if (ch.enabled.isOn()) {
ch.connect();
}
else {
// ch.closeChannel();
ch.dispose();
}
}
} // end onMousePressed
}
}
//---------------------------------------------------------------------------------------------