Details | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 15 | arune | 1 | /* |
| 2 | * File created by Jimmy |
||
| 3 | * at 2003-sep-02 21:08:53 |
||
| 4 | */ |
||
| 5 | package Macbeth.Modules.modARNE; |
||
| 6 | |||
| 7 | import Macbeth.System.*; |
||
| 8 | import Macbeth.XML.*; |
||
| 9 | import Macbeth.Utilities.*; |
||
| 10 | import javax.comm.*; |
||
| 11 | import java.util.*; |
||
| 12 | import java.io.*; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * Macbeth module that enables macbeth modules to communicate |
||
| 16 | * with ARNE nodes via the serial port. |
||
| 17 | * @author Jimmy |
||
| 18 | * @author arune |
||
| 19 | */ |
||
| 20 | public class modARNE extends MbModule { |
||
| 21 | //TODO: timeout, if an arne-packet is not completely received during this time, that packet should be flushed |
||
| 22 | |||
| 23 | //problem med att skicka två paket (eller fler) i följd, CTS från avr kommer först när första paketet skickats |
||
| 24 | //klart, då har redan paket två placerats i bufferten |
||
| 25 | //jag har försökt med handskakning (dvs hårdvaran ska ta hand om CTS och sluta skicka) men det vill inte fungera |
||
| 26 | //testat kolla getOutputBufferSize, men den verkar alltid vara 0 |
||
| 27 | //testat eventen OUTPUT_BUFFER_EMPTY, för att hålla en flagga så man vet när data finns i buffer, men nix |
||
| 28 | |||
| 29 | //This will handle all serial port IO |
||
| 30 | private SerialPortIO spr; |
||
| 31 | //Which byte are we currently receiving from ARNE? |
||
| 32 | private byte currentReceiveByte; |
||
| 33 | //A temporary ARNE packet that will be used when receiving from ARNE |
||
| 34 | private ARNEPacket apTempARNE; |
||
| 35 | //A temporary holder for pc host byte |
||
| 36 | private byte pcHost=0; |
||
| 37 | //A temporary holder fo pc module byte |
||
| 38 | private byte pcModule=0; |
||
| 39 | //A temporary ARNE packet that will be used when receiving XML from macbeth |
||
| 40 | private ARNEPacket apTempMacbeth; |
||
| 41 | //A list of Macbeth module names, where each name is mapped to a byte |
||
| 42 | private HashMap moduleMappings; |
||
| 43 | //A list of Macbeth kernel names, where each name is mapped to a byte |
||
| 44 | private HashMap kernelMappings; |
||
| 45 | //A list of ARNE node names, where each name is mapped to a byte |
||
| 46 | private HashMap nodeMappings; |
||
| 47 | //This flag is set true when an ARNE packet in XML format is currently being parsed |
||
| 48 | private boolean parsingARNEXML; |
||
| 49 | |||
| 50 | /** |
||
| 51 | * Creates a new instance of modARNE. |
||
| 52 | */ |
||
| 53 | public modARNE() { |
||
| 54 | //construct MbModule |
||
| 55 | super(); |
||
| 56 | //initial values... |
||
| 57 | currentReceiveByte = 1; |
||
| 58 | apTempARNE = null; |
||
| 59 | apTempMacbeth = null; |
||
| 60 | options.putField("comport", "COM1"); |
||
| 61 | parsingARNEXML = false; |
||
| 62 | //mapping tables |
||
| 63 | moduleMappings = new HashMap(64); |
||
| 64 | kernelMappings = new HashMap(32); |
||
| 65 | nodeMappings = new HashMap(64); |
||
| 66 | //register our packet data handler |
||
| 67 | setPacketDataHandler(new PacketDataHandler()); |
||
| 68 | } |
||
| 69 | |||
| 70 | /** |
||
| 71 | * Gets the name of the component. |
||
| 72 | * @return The name of the component. |
||
| 73 | */ |
||
| 74 | public String name() { |
||
| 75 | return "ARNE"; |
||
| 76 | } |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Gets the description of the component. |
||
| 80 | * @return The description of the component. |
||
| 81 | */ |
||
| 82 | public String description() { |
||
| 83 | return "Allows for communication between modules and ARNE nodes"; |
||
| 84 | } |
||
| 85 | |||
| 86 | /** |
||
| 87 | * This method sets default values on all _required_ data |
||
| 88 | * fields in the data repository. |
||
| 89 | */ |
||
| 90 | public void initDataFields() { |
||
| 91 | //set default values on all REQUIRED option fields before starting up subsystems |
||
| 92 | options.putField("comport", "COM1"); |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * Starts up this module. |
||
| 97 | */ |
||
| 98 | public void startup() throws MbStartupException { |
||
| 99 | //start up MbModule |
||
| 100 | super.startup(); |
||
| 101 | |||
| 102 | //init serial port |
||
| 103 | spr = new SerialPortIO(options.getField("comport")); |
||
| 104 | try { |
||
| 105 | spr.start(); |
||
| 106 | } catch (IOException e) { |
||
| 107 | //we cannot start up if serial port failed to initialize |
||
| 108 | spr.stop(); |
||
| 109 | throw new MbStartupException("The serialport failed to initialize (" + e.getMessage() + ")"); |
||
| 110 | } |
||
| 111 | |||
| 112 | //get all module mappings from data repository |
||
| 113 | DataRepository.DataList modules = dataRepository.getList("modulemappings"); |
||
| 114 | Iterator it = modules.items(); |
||
| 115 | while (it.hasNext()) { |
||
| 116 | DataRepository.DataListItem item = (DataRepository.DataListItem) it.next(); |
||
| 117 | String name = item.getField("name"); |
||
| 118 | UByte b = UByte.parseUByte(item.getField("byte")); |
||
| 119 | moduleMappings.put(b, name); |
||
| 120 | } |
||
| 121 | |||
| 122 | //get all kernel mappings from data repository |
||
| 123 | DataRepository.DataList kernels = dataRepository.getList("kernelmappings"); |
||
| 124 | it = kernels.items(); |
||
| 125 | while (it.hasNext()) { |
||
| 126 | DataRepository.DataListItem item = (DataRepository.DataListItem) it.next(); |
||
| 127 | String name = item.getField("name"); |
||
| 128 | UByte b = UByte.parseUByte(item.getField("byte")); |
||
| 129 | kernelMappings.put(b, name); |
||
| 130 | } |
||
| 131 | |||
| 132 | //get all kernel mappings from data repository |
||
| 133 | DataRepository.DataList nodes = dataRepository.getList("nodemappings"); |
||
| 134 | it = nodes.items(); |
||
| 135 | while (it.hasNext()) { |
||
| 136 | DataRepository.DataListItem item = (DataRepository.DataListItem) it.next(); |
||
| 137 | String name = item.getField("name"); |
||
| 138 | UByte b = UByte.parseUByte(item.getField("byte")); |
||
| 139 | nodeMappings.put(name, b); |
||
| 140 | } |
||
| 141 | |||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Shuts down this module. |
||
| 146 | */ |
||
| 147 | public void shutdown() { |
||
| 148 | //shut down MbModule |
||
| 149 | super.shutdown(); |
||
| 150 | spr.stop(); |
||
| 151 | } |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Takes care of bytes that we've received from ARNE. |
||
| 155 | * @param bytes The received bytes. |
||
| 156 | */ |
||
| 157 | protected void handleIncomingBytes(byte[] bytes) { |
||
| 158 | for (int i=0; i<bytes.length; i++) { |
||
| 159 | //byte1 is arneheader byte (control bits and data len) |
||
| 160 | if (currentReceiveByte==1) { |
||
| 161 | //check control bits |
||
| 162 | if ((bytes[i] & 0xA0) == 0xA0) { |
||
| 163 | apTempARNE = new ARNEPacket(); |
||
| 164 | //if OK, save byte and go on |
||
| 165 | apTempARNE.header[0] = new UByte(bytes[i]); |
||
| 166 | //this byte determines data length, |
||
| 167 | //so allocate memory now |
||
| 168 | int size = bytes[i] & 0x0F; |
||
| 169 | if (size>0 && size<ARNEPacket.MAX_ARNE_DATALEN) { |
||
| 170 | apTempARNE.setDataLen(size); |
||
| 171 | currentReceiveByte++; |
||
| 172 | } |
||
| 173 | } |
||
| 174 | } |
||
| 175 | //byte2 is PC-host byte |
||
| 176 | else if (currentReceiveByte==2) { |
||
| 177 | pcHost = UByte.fromShort((short)bytes[i]).byteValue(); |
||
| 178 | currentReceiveByte++; |
||
| 179 | } |
||
| 180 | //byte3 is PC-module byte |
||
| 181 | else if (currentReceiveByte==3) { |
||
| 182 | pcModule = UByte.fromShort((short)bytes[i]).byteValue(); |
||
| 183 | currentReceiveByte++; |
||
| 184 | } |
||
| 185 | //the rest are arne-data bytes |
||
| 186 | else { |
||
| 187 | //check if we're still filling data buffer |
||
| 188 | if (currentReceiveByte <= apTempARNE.getDataLen()+3) { |
||
| 189 | //if so, just continue to fill |
||
| 190 | apTempARNE.data[currentReceiveByte-4] = new UByte(bytes[i]); |
||
| 191 | //was this the last byte? |
||
| 192 | if (currentReceiveByte==apTempARNE.getDataLen()+3) { |
||
| 193 | //if so, handle all data, and restart |
||
| 194 | handleIncomingARNEPacket(apTempARNE, pcHost, pcModule); |
||
| 195 | currentReceiveByte = 1; |
||
| 196 | } else { |
||
| 197 | //else, go on |
||
| 198 | currentReceiveByte++; |
||
| 199 | } |
||
| 200 | } |
||
| 201 | } |
||
| 202 | } |
||
| 203 | } |
||
| 204 | |||
| 205 | /** |
||
| 206 | * Takes care of an ARNE packet that've been received from ARNE. |
||
| 207 | * @param ap The received ARNE packet. |
||
| 208 | * @param pcHost The PC Host byte. This will be translated to a Macbeth Kernel name. |
||
| 209 | * @param pcModule The PC Module byte. This will be translated to a Macbeth Module name. |
||
| 210 | */ |
||
| 211 | private void handleIncomingARNEPacket(ARNEPacket ap, byte pcHost, byte pcModule) { |
||
| 212 | //_debug.println("handling incoming ARNE packet"); |
||
| 213 | MbPacket p = new MbPacket(); |
||
| 214 | p.getDestination().setKernel((String)kernelMappings.get(new UByte(pcHost))); |
||
| 215 | p.getDestination().setModule((String)moduleMappings.get(new UByte(pcModule))); |
||
| 216 | if (p.getDestination().getKernel()!=null & p.getDestination().getModule()!=null) { |
||
| 217 | p.setContents("<arnepacket bytes=\"" + ap.data.length + "\">"); |
||
| 218 | for (int i=0; i<ap.data.length; i++) { |
||
| 219 | p.appendContents("<byte id=\"" + Integer.toString(i+1) + "\" value=\"" + ap.data[i].toString() + "\" />"); |
||
| 220 | } |
||
| 221 | p.appendContents("</arnepacket>"); |
||
| 222 | sendPacket(p); |
||
| 223 | } else { |
||
| 224 | _errors.println("Incoming arne packet had unknown host- and/or module-bytes!"); |
||
| 225 | _errors.println("(host: " + UByte.toString(pcHost) + ", module: " + UByte.toString(pcModule) + ")"); |
||
| 226 | } |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Sends an ARNE packet to the ARNE PC node. |
||
| 231 | * @param ap The ARNE packet to send. |
||
| 232 | */ |
||
| 233 | private void sendARNEPacket(ARNEPacket ap) { |
||
| 234 | //_debug.println("sending ARNE packet:"); |
||
| 235 | byte[] bytes = new byte[2+ap.getDataLen()]; |
||
| 236 | bytes[0] = ap.header[0].byteValue(); |
||
| 237 | bytes[1] = ap.header[1].byteValue(); |
||
| 238 | for (int i=0; i<ap.getDataLen(); i++) { |
||
| 239 | bytes[2+i] = ap.data[i].byteValue(); |
||
| 240 | //_debug.println(" byte" + Integer.toString(i) + "=" + ap.data[i].toString()); |
||
| 241 | } |
||
| 242 | try { |
||
| 243 | spr.sendBytes(bytes); |
||
| 244 | } catch (IOException e) { |
||
| 245 | _errors.println("An ARNE packet could not be delivered to the ARNE bus due to I/O-errors! (Exception was '" + e + "')"); |
||
| 246 | } |
||
| 247 | } |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Takes care of XML-data found in incoming packets. |
||
| 251 | */ |
||
| 252 | private class PacketDataHandler implements XMLDataHandler { |
||
| 253 | /** |
||
| 254 | * Called when start of a new element is found in the XML-data. |
||
| 255 | * Ex: <name attr1="value1" attr2="value2"> |
||
| 256 | * Element name would then be "name" and attribute list |
||
| 257 | * would contain "value1" and "value2" mapped to the attribute |
||
| 258 | * names "attr1" and "attr2". |
||
| 259 | * @param element The name of the element. |
||
| 260 | * @param attributes The element attributes. |
||
| 261 | */ |
||
| 262 | public void XMLstartElement(String element, HashMap attributes) { |
||
| 263 | /** |
||
| 264 | * Ex: <arnepacket bytes="5" destnode="25"> |
||
| 265 | * This is the start of an ARNE packet that some other module |
||
| 266 | * is sending to us. |
||
| 267 | */ |
||
| 268 | if (element.equals("arnepacket") && attributes.containsKey("bytes") && attributes.containsKey("destnode")) { |
||
| 269 | short bytes = Short.parseShort((String)attributes.get("bytes")); |
||
| 270 | String destNode = (String)attributes.get("destnode"); |
||
| 271 | //check if specified ARNE node is valid |
||
| 272 | if (nodeMappings.containsKey(destNode)) { |
||
| 273 | //check if number of bytes is valid |
||
| 274 | if (bytes>0 && bytes<=ARNEPacket.MAX_ARNE_DATALEN) { |
||
| 275 | parsingARNEXML = true; |
||
| 276 | apTempMacbeth = new ARNEPacket(); |
||
| 277 | apTempMacbeth.setDataLen(bytes); |
||
| 278 | apTempMacbeth.setNodeAddress(((UByte)nodeMappings.get(destNode)).byteValue()); |
||
| 279 | } else { |
||
| 280 | //invalid number of bytes specified (too few or too many) |
||
| 281 | _errors.println("Invalid number of bytes specified in a packet!"); |
||
| 282 | } |
||
| 283 | } else { |
||
| 284 | //invalid destination ARNE node was specified |
||
| 285 | _errors.println("A packet was addressed to the ARNE node '" + destNode + "', which is unknown!"); |
||
| 286 | } |
||
| 287 | } |
||
| 288 | /** |
||
| 289 | * Ex: <byte id="1" value="5"> |
||
| 290 | * This is a byte inside an ARNE packet. |
||
| 291 | */ |
||
| 292 | else if (element.equals("byte") && attributes.containsKey("id") && attributes.containsKey("value")) { |
||
| 293 | //only care about byte-tags while parsing valid ARNE packets |
||
| 294 | if (parsingARNEXML) { |
||
| 295 | short id = Short.parseShort((String)attributes.get("id")); |
||
| 296 | //if id is valid |
||
| 297 | if (id>0 && id<=apTempMacbeth.getDataLen()) { |
||
| 298 | //set data in arne packet |
||
| 299 | //int intval = Integer.parseInt((String)attributes.get("value")); |
||
| 300 | apTempMacbeth.data[id-1] = UByte.parseUByte((String)attributes.get("value")); |
||
| 301 | } else { |
||
| 302 | _errors.println("Invalid byte IDs specified in a packet"); |
||
| 303 | parsingARNEXML = false; |
||
| 304 | } |
||
| 305 | } else { |
||
| 306 | //we are not parsing a valid ARNE packet at the moment, so ignore byte-tags |
||
| 307 | } |
||
| 308 | } |
||
| 309 | } |
||
| 310 | |||
| 311 | /** |
||
| 312 | * Called when end of an element was found in the XML-data. |
||
| 313 | * Ex: </name> or <test attr="value" /> |
||
| 314 | * @param element The name of the element. |
||
| 315 | */ |
||
| 316 | public void XMLendElement(String element) { |
||
| 317 | /** |
||
| 318 | * Ex: </arnepacket> |
||
| 319 | * End of a received ARNE packet. At this point we should send |
||
| 320 | * the whole received packet to the ARNE bus. |
||
| 321 | */ |
||
| 322 | if (element.equals("arnepacket")) { |
||
| 323 | if (parsingARNEXML) { |
||
| 324 | //check if all bytes have been parsed |
||
| 325 | boolean allBytesParsed = true; |
||
| 326 | for (int i=0; i<apTempMacbeth.getDataLen(); i++) { |
||
| 327 | if (apTempMacbeth.data[i]==null) { |
||
| 328 | allBytesParsed = false; |
||
| 329 | } |
||
| 330 | } |
||
| 331 | if (allBytesParsed) { |
||
| 332 | //if that is the case, send the packet to the ARNE bus now |
||
| 333 | parsingARNEXML = false; |
||
| 334 | sendARNEPacket(apTempMacbeth); |
||
| 335 | //_debug.println("Ive got packet!"); |
||
| 336 | } else { |
||
| 337 | //if not all bytes have been parsed, packet was invalid |
||
| 338 | _errors.println("Invalid packet received! (bytes were missing)"); |
||
| 339 | } |
||
| 340 | } |
||
| 341 | } |
||
| 342 | } |
||
| 343 | |||
| 344 | public void XMLelementData(String data) {} |
||
| 345 | public void XMLdocumentStart() {} |
||
| 346 | public void XMLdocumentEnd() {} |
||
| 347 | } |
||
| 348 | |||
| 349 | |||
| 350 | /** |
||
| 351 | * A class for handling all serial port IO. |
||
| 352 | */ |
||
| 353 | public class SerialPortIO implements Runnable, SerialPortEventListener { |
||
| 354 | private CommPortIdentifier portId; |
||
| 355 | private SerialPort serialPort; |
||
| 356 | private boolean isOutputBufferEmpty; |
||
| 357 | private InputStream inputStream; |
||
| 358 | private OutputStream outputStream; |
||
| 359 | private Thread readThread; |
||
| 360 | private String portName; |
||
| 361 | //a queue for byte sequences that're waiting to be sent |
||
| 362 | private LinkedList byteQueue; |
||
| 363 | //a thread that makes sure queued byte sequences are sent later |
||
| 364 | private ByteQueueHandler byteQueueHandler; |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Creates a new instance of SerialPortIO. |
||
| 368 | * @param portname The name of the port to use. |
||
| 369 | */ |
||
| 370 | public SerialPortIO(String portname) { |
||
| 371 | portName = portname; |
||
| 372 | portId = null; |
||
| 373 | serialPort = null; |
||
| 374 | inputStream = null; |
||
| 375 | outputStream = null; |
||
| 376 | byteQueue = new LinkedList(); |
||
| 377 | byteQueueHandler = new ByteQueueHandler(byteQueue); |
||
| 378 | isOutputBufferEmpty = true; |
||
| 379 | } |
||
| 380 | |||
| 381 | /** |
||
| 382 | * Starts listening for data on the port. Also enables |
||
| 383 | * outbound data transmission on the port. |
||
| 384 | * @throws IOException When, for some reason, the port fails |
||
| 385 | * to initialize. |
||
| 386 | */ |
||
| 387 | public void start() throws IOException { |
||
| 388 | try { |
||
| 389 | portId = CommPortIdentifier.getPortIdentifier(portName); |
||
| 390 | } catch (NoSuchPortException e) { |
||
| 391 | throw new IOException("The port " + portName + " was not found on this system!"); |
||
| 392 | } |
||
| 393 | try { |
||
| 394 | serialPort = (SerialPort) portId.open("modARNE", 2000); |
||
| 395 | } catch (PortInUseException e) { |
||
| 396 | throw new IOException("The port " + portName + " is already in use! ('" + e + "')"); |
||
| 397 | } |
||
| 398 | try { |
||
| 399 | inputStream = serialPort.getInputStream(); |
||
| 400 | outputStream = serialPort.getOutputStream(); |
||
| 401 | } catch (IOException e) { |
||
| 402 | e.printStackTrace(); |
||
| 403 | } |
||
| 404 | try { |
||
| 405 | serialPort.addEventListener(this); |
||
| 406 | } catch (TooManyListenersException e) { |
||
| 407 | e.printStackTrace(); |
||
| 408 | } |
||
| 409 | serialPort.notifyOnDataAvailable(true); |
||
| 410 | //serialPort.notifyOnOutputEmpty(true); //serialEvent for when the output buffer is empty (a new packet can be sent) |
||
| 411 | try { |
||
| 412 | serialPort.setSerialPortParams(19200,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE); |
||
| 413 | //serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); |
||
| 414 | serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); |
||
| 415 | } catch (UnsupportedCommOperationException e) { |
||
| 416 | e.printStackTrace(); |
||
| 417 | } |
||
| 418 | readThread = new Thread(this); |
||
| 419 | readThread.start(); |
||
| 420 | } |
||
| 421 | |||
| 422 | /** |
||
| 423 | * Stops listening for data on the port. |
||
| 424 | */ |
||
| 425 | public void stop() { |
||
| 426 | byteQueueHandler.stopWorking(); |
||
| 427 | if (serialPort!=null) { |
||
| 428 | serialPort.close(); |
||
| 429 | } |
||
| 430 | } |
||
| 431 | |||
| 432 | /** |
||
| 433 | * Will be called from the thread. |
||
| 434 | */ |
||
| 435 | public void run() { |
||
| 436 | //don't do shit here. we will be notified about incoming data |
||
| 437 | try { |
||
| 438 | Thread.sleep(2000); |
||
| 439 | } catch (InterruptedException e) { |
||
| 440 | } |
||
| 441 | } |
||
| 442 | |||
| 443 | /** |
||
| 444 | * Will be called when an event is triggered by the port. |
||
| 445 | * @param event The event. |
||
| 446 | */ |
||
| 447 | public void serialEvent(SerialPortEvent event) { |
||
| 448 | switch(event.getEventType()) { |
||
| 449 | case SerialPortEvent.BI: |
||
| 450 | break; |
||
| 451 | case SerialPortEvent.OE: |
||
| 452 | break; |
||
| 453 | case SerialPortEvent.FE: |
||
| 454 | break; |
||
| 455 | case SerialPortEvent.PE: |
||
| 456 | break; |
||
| 457 | case SerialPortEvent.CD: |
||
| 458 | break; |
||
| 459 | case SerialPortEvent.CTS: |
||
| 460 | break; |
||
| 461 | case SerialPortEvent.DSR: |
||
| 462 | break; |
||
| 463 | case SerialPortEvent.RI: |
||
| 464 | break; |
||
| 465 | case SerialPortEvent.OUTPUT_BUFFER_EMPTY: |
||
| 466 | //isOutputBufferEmpty = true; |
||
| 467 | break; |
||
| 468 | case SerialPortEvent.DATA_AVAILABLE: |
||
| 469 | //there is incoming data available on the port, so read it |
||
| 470 | byte[] readBuffer = new byte[20]; |
||
| 471 | |||
| 472 | try { |
||
| 473 | int numBytes = 0; |
||
| 474 | while (inputStream.available() > 0) { |
||
| 475 | numBytes = inputStream.read(readBuffer); |
||
| 476 | } |
||
| 477 | byte[] readBufferDummy = new byte[numBytes]; |
||
| 478 | for (int i = 0; i < numBytes; i++) { |
||
| 479 | readBufferDummy[i] = readBuffer[i]; |
||
| 480 | } |
||
| 481 | handleIncomingBytes(readBufferDummy); |
||
| 482 | } catch (IOException e) { |
||
| 483 | } |
||
| 484 | break; |
||
| 485 | } |
||
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * Tries to sends a byte array to the port. If the CTS-flag is not |
||
| 490 | * set, the byte sequence will be inserted into a queue and sent |
||
| 491 | * later, when the CTS-flag has been set again. |
||
| 492 | * @param bytes The bytes to send. |
||
| 493 | * @throws IOException if an I/O-error occurs while trying |
||
| 494 | * to send the data. |
||
| 495 | */ |
||
| 496 | public void sendBytes(byte[] bytes) throws IOException { |
||
| 497 | //check if it is clear to send and no other bytes are in send-queue |
||
| 498 | //if-case is commented, always put in queue |
||
| 499 | /* if (serialPort.isCTS() && byteQueue.isEmpty()) { |
||
| 500 | //if everything's OK, send the bytes immediately |
||
| 501 | writeBytes(bytes); |
||
| 502 | }*/ |
||
| 503 | |||
| 504 | //we cannot send yet |
||
| 505 | // else { |
||
| 506 | //so insert the new bytes into the queue instead |
||
| 507 | if (byteQueue.size()<100) { |
||
| 508 | byteQueue.addLast(bytes); |
||
| 509 | } |
||
| 510 | else { |
||
| 511 | _errors.println("There are more than 100 byte sequences in send-queue. The queue will be cleared. Please check your serial port hardware!!"); |
||
| 512 | byteQueue.clear(); |
||
| 513 | } |
||
| 514 | //and start the byte queue handler if not already started |
||
| 515 | if (!byteQueueHandler.isWorking()) { |
||
| 516 | byteQueueHandler.startWorking(); |
||
| 517 | } |
||
| 518 | //_debug.println("An ARNE packet was inserted in the send-queue! It will be sent as soon as possible."); |
||
| 519 | // } |
||
| 520 | } |
||
| 521 | |||
| 522 | /** |
||
| 523 | * Sends a byte array to the port. No flow control will be used!! |
||
| 524 | * You must make sure the CTS-flag is set before calling this! |
||
| 525 | * @param bytes The bytes to send. |
||
| 526 | * @throws IOException if an I/O-error occurs while trying |
||
| 527 | * to send the data. |
||
| 528 | */ |
||
| 529 | private synchronized void writeBytes(byte[] bytes) throws IOException { |
||
| 530 | if (outputStream != null) { |
||
| 531 | outputStream.write(bytes); |
||
| 532 | } |
||
| 533 | else { |
||
| 534 | _errors.println("Serious error! Cannot write bytes to serial port since the outputstream doesn't exist!"); |
||
| 535 | } |
||
| 536 | } |
||
| 537 | |||
| 538 | |||
| 539 | //this is a thread that will take care of the byte queue |
||
| 540 | private class ByteQueueHandler extends Thread { |
||
| 541 | private LinkedList byteQueue; |
||
| 542 | private boolean runThread; |
||
| 543 | |||
| 544 | public ByteQueueHandler(LinkedList byteQueue) { |
||
| 545 | super("ByteQueueHandler"); |
||
| 546 | this.byteQueue = byteQueue; |
||
| 547 | } |
||
| 548 | |||
| 549 | public void startWorking() { |
||
| 550 | runThread = true; |
||
| 551 | //start thread (it will call our run-method in a new thread) |
||
| 552 | start(); |
||
| 553 | } |
||
| 554 | |||
| 555 | public void stopWorking() { |
||
| 556 | runThread = false; |
||
| 557 | } |
||
| 558 | |||
| 559 | public boolean isWorking() { |
||
| 560 | return runThread; |
||
| 561 | } |
||
| 562 | |||
| 563 | //This is called by the thread when running. |
||
| 564 | public void run() { |
||
| 565 | //loop while run-flag is set true and queue is not empty |
||
| 566 | while (runThread) { |
||
| 567 | //if queue is not empty and CTS-flag is set |
||
| 568 | //if (!byteQueue.isEmpty() && serialPort.isCTS() && isOutputBufferEmpty) { |
||
| 569 | //if (!byteQueue.isEmpty()) { |
||
| 570 | if (!byteQueue.isEmpty() && serialPort.isCTS()) { |
||
| 571 | //let's send the next arne packet in queue |
||
| 572 | try { |
||
| 573 | writeBytes((byte[])byteQueue.removeFirst()); |
||
| 574 | //isOutputBufferEmpty = false; |
||
| 575 | //_debug.println("nr of bytes in buffer: " + serialPort.getOutputBufferSize()); |
||
| 576 | //is the queue empty now? |
||
| 577 | if (byteQueue.isEmpty()) { |
||
| 578 | //_debug.println("All ARNE-packets in send-queue have now been sent!"); |
||
| 579 | } |
||
| 580 | } catch (IOException e) { |
||
| 581 | _errors.println("I/O-error while trying to write bytes to port (Exception was '" + e + "')"); |
||
| 582 | } |
||
| 583 | try { |
||
| 584 | sleep(50); |
||
| 585 | } catch (InterruptedException e) { |
||
| 586 | stopWorking(); |
||
| 587 | } |
||
| 588 | } |
||
| 589 | //CTS-flag hasn't been set yet, or else the queue is empty |
||
| 590 | else { |
||
| 591 | //so try to get some sleep for a while =) |
||
| 592 | try { |
||
| 593 | sleep(50); |
||
| 594 | } catch (InterruptedException e) { |
||
| 595 | stopWorking(); |
||
| 596 | } |
||
| 597 | } |
||
| 598 | } |
||
| 599 | } |
||
| 600 | } |
||
| 601 | } |
||
| 602 | |||
| 603 | } |