Subversion Repositories HomeAutomation

Rev

Rev 641 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  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. }
  604.