-
Notifications
You must be signed in to change notification settings - Fork 0
/
Coordinator.java
359 lines (307 loc) · 11.6 KB
/
Coordinator.java
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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
package org.apparatus_templi;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
/**
* Coordinator
* @author Jonathan Nelson <ciasaboark@gmail.com>
*
*/
public class Coordinator {
private static String incommingBuffer;
private static ArrayList<String> remoteModules = new ArrayList<String>();
// private static ArrayList<Driver> runningDrivers;
private static int portNum;
private static final int defaultPort = 2024;
private static String serialPortName = "";
private static final String TAG = "Coordinator";
private static SerialConnectionOld serialConnectionOld = null;
private static boolean ioReady = false;
//bitmask flags for the transmission header
private static byte textTransmission = (byte)0b0010_0000;
private static byte binTransmission = (byte)0b1010_0000;
private static byte protocolVersion = (byte)0b0000_0000; //current protocol is version 0
private static String headerSeperator = ":";
private static String footer = "\n";
private static SerialConnection serialConnection;
private static HashMap<String, Driver> runningDrivers = new HashMap<String, Driver>();
/**
* Pass a message to the driver specified by name.
* @param driverName the unique name of the driver
* @param message the message to pass
*/
private void passMessage(String driverName, String message) {
//
}
/**
* Reads a single byte of data from the incoming serial connection
* @return the Byte value of the read byte, null if there was nothing
* to read or if the read failed.
*/
private Byte readSerial() {
//TODO
return null;
}
/**
* Checks the serial connection to see if there is any data available for reading
* @return true if data is available for reading, false otherwise
*/
private boolean serialDataAvailable() {
//TODO
return false;
}
/**
* Sends the given command to a specific remote module
* @param name the unique name of the remote module
* @param command the command to send to the remote module
*/
public static synchronized void sendCommand(String name, String command) {
ioReady = true;
//the arduino expects chars as 1 byte instead of two, convert command
//+ to ascii byte array, then tack on the header, name, and footer
byte headerByte = (byte)(textTransmission | protocolVersion);
byte[] destinationBytes = {0b0};
byte[] headerSeperatorByte = {0b0};
byte[] commandBytes = {0b0};
byte[] footerByte = {0b0};
boolean sendMessage = true;
//convert the destination address to ascii byte array
try {
destinationBytes = name.getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, "error converting remote module name '" + name + "' to US-ASCII encoding.");
sendMessage = false;
}
//convert the destination separator to ascii byte array
try {
headerSeperatorByte = headerSeperator.getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, "error converting header seperator to ascii byte array.");
sendMessage = false;
}
//convert the command to ascii byte array
try {
commandBytes = command.getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, "error converting command '" + command +"' to US-ASCII encoding.");
sendMessage = false;
}
//convert the footer to ascii byte
try {
footerByte = footer.getBytes("US-ASCII");
} catch (UnsupportedEncodingException e) {
Log.w(TAG, "error converting message footer to US-ASCII encoding");
sendMessage = false;
}
if (sendMessage) {
ByteBuffer bBuffer = ByteBuffer.allocate(destinationBytes.length + headerSeperatorByte.length + commandBytes.length + footerByte.length);
// bBuffer.put(headerByte);
bBuffer.put(destinationBytes);
bBuffer.put(headerSeperatorByte);
bBuffer.put(commandBytes);
bBuffer.put(footerByte);
Log.d(TAG, "sending bytes 0x" + DatatypeConverter.printHexBinary(bBuffer.array()));
Log.d(TAG, "sending ascii string '" + new String(bBuffer.array()) + "'");
serialConnection.writeData(bBuffer.array());
} else {
Log.w(TAG, "error converting message to ascii byte[]. Message not sent");
}
}
/*
* Protected methods. The drivers should make use of these
*/
/**
* Sends a message to a remote module and waits waitPeriod seconds for a response.
* @param name the unique name of the remote module
* @param command the command to send to the remote module
* @param waitPeriod how many seconds to wait for a response. Maximum period to wait
* is 6 seconds.
* @return the response string of the remote module or null if no response was found
*/
public static synchronized String sendCommandAndWait(String name, String command, int waitPeriod) {
//TODO: this could easily be abused by the drivers to bring down the system. It might need
//+ to be removed, or to limit the number of times any driver can call this method in a given
//+ time period.
return null;
}
/**
* Sends binary data over the serial connection
* @param moduleName the unique name of the remote module
* @param data command the binary data to send
*/
public static synchronized void sendBinary(String moduleName, byte[] data) {
}
public static synchronized void setIoReady(boolean state) {
ioReady = state;
}
/**
* Stores the given data to persistent storage. Data is tagged with both the driver
* name as well as a data tag.
* @param driverName the name of the module
* @param dataTag a tag to assign to this data. This tag should be specific for each data block
* that your driver stores. If there already exits data for the given tag the old data
* will be overwritten.
* @param data the string of data to store
* @return -1 if data overwrote information from a previous tag. 1 if data was written successfully.
* 0 if the data could not be written.
*/
public static synchronized int storeData(String driverName, String dataTag, String data) {
return 0;
}
/**
* Returns data previously stored under the given module name and tag.
* @param driverName the name of the calling driver
* @param dataTag the tag to uniquely identify the data
* @return the stored String data, or null if no data has been stored under the given driver name
* and tag.
*/
public static synchronized String readData(String driverName, String dataTag) {
return null;
}
/**
* Checks the list of known remote modules. If the module is not present the Coordinator
* may re-query the remote modules for updates.
* @param moduleName the name of the remote module to check for
* @return true if the remote module is known to be up, false otherwise
*/
public static synchronized boolean isModulePresent(String moduleName) {
boolean result = false;
for (String name: remoteModules) {
if (name.equals(moduleName)) {
result = true;
break;
}
}
return result;
}
//TODO change this to a singleton
public Coordinator() {
super();
incommingBuffer = "";
}
public static void main(String argv[]) {
//Using apache commons cli to parse the command line options
Options options = new Options();
options.addOption("help", false, "Display this help message.");
Option portOption = OptionBuilder.withArgName("port")
.hasArg()
.withDescription("Bind the server to the given port number")
.create("port");
options.addOption(portOption);
Option serialOption = OptionBuilder.withArgName("serial")
.hasArg()
.withDescription("Connect to the arduino on serial interface")
.create("serial");
options.addOption(serialOption);
CommandLineParser cliParser = new org.apache.commons.cli.PosixParser();
try {
CommandLine cmd = cliParser.parse(options, argv);
if (cmd.hasOption("help")) {
//show help message and exit
HelpFormatter formatter = new HelpFormatter();
formatter.setOptPrefix("--");
formatter.setLongOptPrefix("--");
formatter.printHelp(TAG, options);
System.exit(0);
}
if (cmd.hasOption("port")) {
try {
portNum = Integer.valueOf(cmd.getOptionValue("port"));
} catch (IllegalArgumentException e) {
Log.e("Coordinator", "Bad port number given, setting to default value");
portNum = defaultPort;
}
} else {
Log.d(TAG, "default port " + defaultPort + " selected");
portNum = defaultPort;
}
//try to guess a good serial port if we weren't given one
if (cmd.hasOption("serial")) {
serialPortName = cmd.getOptionValue("serial");
} else {
String osName = System.getProperty("os.name","").toLowerCase();
if ( osName.startsWith("windows") ) {
Log.d(TAG, "detected windows OS, default serial port COM1");
serialPortName = "COM1";
} else if (osName.startsWith("linux")) {
Log.d(TAG, "detected linux OS, default serial port /dev/ttyUSB0");
serialPortName = "/dev/ttyUSB0";
} else if ( osName.startsWith("mac") ) {
Log.d(TAG, "detected mac OS, default serial port /dev/tty.usbmodemfa121");
serialPortName = "/dev/tty.usbmodemfa121";
} else {
Log.w(TAG, "Could not guess a good default serial port, trying COM1");
serialPortName = "COM1";
}
}
} catch (ParseException e) {
System.out.println("Error processing options: " + e.getMessage());
new HelpFormatter().printHelp("Diff", options);
Log.e(TAG, "Error parsing command line options, exiting");
System.exit(1);
}
// //setup the serial connection
// serialConnection = new SerialConnection();
// if (serialConnection.connect(serialPortName)) {
// if (serialConnection.initIOStream() == true) {
// serialConnection.initListener();
// }
// } else {
// //writing to both STDERR and the log file
// String errMsg = "Unable to open serial port " + serialPortName + ", exiting";
// System.err.println(errMsg);
// Log.e(TAG, errMsg);
// System.exit(1);
// }
serialConnection = new SerialConnection();
if (!serialConnection.initialize()) {
Log.e(TAG, "could not connect to serial port, exiting");
System.err.println("could not connect to serial port, exiting");
System.exit(1);
}
//start the drivers
LedFlash driver1 = new LedFlash();
runningDrivers.put(driver1.getModuleName(), (Driver)driver1);
//right now just add the driver name to the remote module list
remoteModules.add(driver1.getModuleName());
(new Thread(driver1)).start();
//enter main loop
while (true) {
//wait for input or output to be ready
while (!ioReady) {
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ioReady = false;
Thread.yield();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
* TODO:
* -wait for the local arduino to respond with "READY" on the serial line
* -broadcast a message to every remote module asking for their name
* -store those names
* -load all drivers
* -start the web server to listen for connections from a frontend
* -enter infinite loop checking for input from the local arduino
*/
}
}