001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2012. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007package edu.wpi.first.wpilibj;
008
009import java.io.UnsupportedEncodingException;
010
011import java.nio.ByteOrder;
012import java.nio.IntBuffer;
013import java.nio.ByteBuffer;
014
015import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
016import edu.wpi.first.wpilibj.communication.UsageReporting;
017import edu.wpi.first.wpilibj.hal.HALLibrary;
018import edu.wpi.first.wpilibj.hal.HALUtil;
019import edu.wpi.first.wpilibj.hal.SerialPortJNI;
020
021/**
022 * Driver for the RS-232 serial port on the RoboRIO.
023 *
024 * The current implementation uses the VISA formatted I/O mode.  This means that
025 *   all traffic goes through the formatted buffers.  This allows the intermingled
026 *   use of print(), readString(), and the raw buffer accessors read() and write().
027 *
028 * More information can be found in the NI-VISA User Manual here:
029 *   http://www.ni.com/pdf/manuals/370423a.pdf
030 * and the NI-VISA Programmer's Reference Manual here:
031 *   http://www.ni.com/pdf/manuals/370132c.pdf
032 */
033public class SerialPort {
034
035    private byte m_port;
036        
037        public enum Port {
038                kOnboard(0), 
039                kMXP(1),
040                kUSB(2);
041                
042                private int value;
043                
044                private Port(int value){
045                        this.value = value;
046                }
047                
048                public int getValue(){
049                        return this.value;
050                }
051        };
052
053    /**
054     * Represents the parity to use for serial communications
055     */
056    public static class Parity {
057
058        /**
059         * The integer value representing this enumeration
060         */
061        public final int value;
062        static final int kNone_val = 0;
063        static final int kOdd_val = 1;
064        static final int kEven_val = 2;
065        static final int kMark_val = 3;
066        static final int kSpace_val = 4;
067        /**
068         * parity: Use no parity
069         */
070        public static final Parity kNone = new Parity(kNone_val);
071        /**
072         * parity: Use odd parity
073         */
074        public static final Parity kOdd = new Parity(kOdd_val);
075        /**
076         * parity: Use even parity
077         */
078        public static final Parity kEven = new Parity(kEven_val);
079        /**
080         * parity: Use mark parity
081         */
082        public static final Parity kMark = new Parity(kMark_val);
083        /**
084         * parity: Use space parity
085         */
086        public static final Parity kSpace = new Parity((kSpace_val));
087
088        private Parity(int value) {
089            this.value = value;
090        }
091    }
092
093    /**
094     * Represents the number of stop bits to use for Serial Communication
095     */
096    public static class StopBits {
097
098        /**
099         * The integer value representing this enumeration
100         */
101        public final int value;
102        static final int kOne_val = 10;
103        static final int kOnePointFive_val = 15;
104        static final int kTwo_val = 20;
105        /**
106         * stopBits: use 1
107         */
108        public static final StopBits kOne = new StopBits(kOne_val);
109        /**
110         * stopBits: use 1.5
111         */
112        public static final StopBits kOnePointFive = new StopBits(kOnePointFive_val);
113        /**
114         * stopBits: use 2
115         */
116        public static final StopBits kTwo = new StopBits(kTwo_val);
117
118        private StopBits(int value) {
119            this.value = value;
120        }
121    }
122
123    /**
124     * Represents what type of flow control to use for serial communication
125     */
126    public static class FlowControl {
127
128        /**
129         * The integer value representing this enumeration
130         */
131        public final int value;
132        static final int kNone_val = 0;
133        static final int kXonXoff_val = 1;
134        static final int kRtsCts_val = 2;
135        static final int kDtrDsr_val = 4;
136        /**
137         * flowControl: use none
138         */
139        public static final FlowControl kNone = new FlowControl(kNone_val);
140        /**
141         * flowcontrol: use on/off
142         */
143        public static final FlowControl kXonXoff = new FlowControl(kXonXoff_val);
144        /**
145         * flowcontrol: use rts cts
146         */
147        public static final FlowControl kRtsCts = new FlowControl(kRtsCts_val);
148        /**
149         * flowcontrol: use dts dsr
150         */
151        public static final FlowControl kDtrDsr = new FlowControl(kDtrDsr_val);
152
153        private FlowControl(int value) {
154            this.value = value;
155        }
156    }
157
158    /**
159     * Represents which type of buffer mode to use when writing to a serial port
160     */
161    public static class WriteBufferMode {
162
163        /**
164         * The integer value representing this enumeration
165         */
166        public final int value;
167        static final int kFlushOnAccess_val = 1;
168        static final int kFlushWhenFull_val = 2;
169        /**
170         * Flush on access
171         */
172        public static final WriteBufferMode kFlushOnAccess = new WriteBufferMode(kFlushOnAccess_val);
173        /**
174         * Flush when full
175         */
176        public static final WriteBufferMode kFlushWhenFull = new WriteBufferMode(kFlushWhenFull_val);
177
178        private WriteBufferMode(int value) {
179            this.value = value;
180        }
181    }
182
183    /**
184     * Create an instance of a Serial Port class.
185     *
186     * @param baudRate The baud rate to configure the serial port.
187         * @param port The Serial port to use
188     * @param dataBits The number of data bits per transfer.  Valid values are between 5 and 8 bits.
189     * @param parity Select the type of parity checking to use.
190     * @param stopBits The number of stop bits to use as defined by the enum StopBits.
191     */
192    public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity, StopBits stopBits) {
193                ByteBuffer status = ByteBuffer.allocateDirect(4);
194                status.order(ByteOrder.LITTLE_ENDIAN);
195        m_port = (byte) port.getValue();
196                
197                SerialPortJNI.serialInitializePort(m_port, status.asIntBuffer());
198                HALUtil.checkStatus(status.asIntBuffer());
199                SerialPortJNI.serialSetBaudRate(m_port, baudRate, status.asIntBuffer());
200                HALUtil.checkStatus(status.asIntBuffer());
201                SerialPortJNI.serialSetDataBits(m_port, (byte) dataBits, status.asIntBuffer());
202                HALUtil.checkStatus(status.asIntBuffer());
203                SerialPortJNI.serialSetParity(m_port, (byte) parity.value, status.asIntBuffer());
204                HALUtil.checkStatus(status.asIntBuffer());
205                SerialPortJNI.serialSetStopBits(m_port, (byte) stopBits.value, status.asIntBuffer());
206                HALUtil.checkStatus(status.asIntBuffer());
207                
208        // Set the default read buffer size to 1 to return bytes immediately
209        setReadBufferSize(1);
210
211        // Set the default timeout to 5 seconds.
212        setTimeout(5.0f);
213
214        // Don't wait until the buffer is full to transmit.
215        setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
216
217        disableTermination();
218
219        UsageReporting.report(tResourceType.kResourceType_SerialPort,0);
220    }
221
222    /**
223     * Create an instance of a Serial Port class. Defaults to one stop bit.
224     *
225     * @param baudRate The baud rate to configure the serial port.
226     * @param dataBits The number of data bits per transfer.  Valid values are between 5 and 8 bits.
227     * @param parity Select the type of parity checking to use.
228     */
229    public SerialPort(final int baudRate, Port port, final int dataBits, Parity parity) {
230        this(baudRate, port, dataBits, parity, StopBits.kOne);
231    }
232
233    /**
234     * Create an instance of a Serial Port class. Defaults to no parity and one
235     * stop bit.
236     *
237     * @param baudRate The baud rate to configure the serial port.
238     * @param dataBits The number of data bits per transfer.  Valid values are between 5 and 8 bits.
239     */
240    public SerialPort(final int baudRate, Port port, final int dataBits) {
241        this(baudRate, port, dataBits, Parity.kNone, StopBits.kOne);
242    }
243
244    /**
245     * Create an instance of a Serial Port class. Defaults to 8 databits,
246     * no parity, and one stop bit.
247     *
248     * @param baudRate The baud rate to configure the serial port.
249     */
250    public SerialPort(final int baudRate, Port port) {
251        this(baudRate, port, 8, Parity.kNone, StopBits.kOne);
252    }
253
254    /**
255     * Destructor.
256     */
257    public void free() {
258                ByteBuffer status = ByteBuffer.allocateDirect(4);
259                status.order(ByteOrder.LITTLE_ENDIAN);
260        SerialPortJNI.serialClose(m_port, status.asIntBuffer());
261                HALUtil.checkStatus(status.asIntBuffer());
262    }
263
264    /**
265     * Set the type of flow control to enable on this port.
266     *
267     * By default, flow control is disabled.
268     * @param flowControl the FlowControl value to use
269     */
270    public void setFlowControl(FlowControl flowControl) {
271                ByteBuffer status = ByteBuffer.allocateDirect(4);
272                status.order(ByteOrder.LITTLE_ENDIAN);
273        SerialPortJNI.serialSetFlowControl(m_port, (byte) flowControl.value, status.asIntBuffer());
274                HALUtil.checkStatus(status.asIntBuffer());              
275    }
276
277    /**
278     * Enable termination and specify the termination character.
279     *
280     * Termination is currently only implemented for receive.
281     * When the the terminator is received, the read() or readString() will return
282     *   fewer bytes than requested, stopping after the terminator.
283     *
284     * @param terminator The character to use for termination.
285     */
286    public void enableTermination(char terminator) {
287                ByteBuffer status = ByteBuffer.allocateDirect(4);
288                status.order(ByteOrder.LITTLE_ENDIAN);
289                SerialPortJNI.serialEnableTermination(m_port, terminator, status.asIntBuffer());
290                HALUtil.checkStatus(status.asIntBuffer());      
291    }
292
293    /**
294     * Enable termination with the default terminator '\n'
295     *
296     * Termination is currently only implemented for receive.
297     * When the the terminator is received, the read() or readString() will return
298     *   fewer bytes than requested, stopping after the terminator.
299     *
300     * The default terminator is '\n'
301     */
302    public void enableTermination() {
303        this.enableTermination('\n');
304    }
305
306    /**
307     * Disable termination behavior.
308     */
309    public void disableTermination() {
310                ByteBuffer status = ByteBuffer.allocateDirect(4);
311                status.order(ByteOrder.LITTLE_ENDIAN);
312                SerialPortJNI.serialDisableTermination(m_port, status.asIntBuffer());
313                HALUtil.checkStatus(status.asIntBuffer());
314    }
315
316    /**
317     * Get the number of bytes currently available to read from the serial port.
318     *
319     * @return The number of bytes available to read.
320     */
321    public int getBytesReceived() {
322                int retVal = 0;
323        ByteBuffer status = ByteBuffer.allocateDirect(4);
324                status.order(ByteOrder.LITTLE_ENDIAN);
325                retVal = SerialPortJNI.serialGetBytesRecieved(m_port, status.asIntBuffer());
326                HALUtil.checkStatus(status.asIntBuffer());
327                return retVal;
328    }
329
330    /**
331     * Read a string out of the buffer. Reads the entire contents of the buffer
332     *
333     * @return The read string
334     */
335    public String readString() {
336        return readString(getBytesReceived());
337    }
338
339    /**
340     * Read a string out of the buffer. Reads the entire contents of the buffer
341     *
342     * @param count the number of characters to read into the string
343     * @return The read string
344     */
345    public String readString(int count) {
346        byte[] out = read(count);
347        try {
348            return new String(out, 0, count, "US-ASCII");
349        } catch (UnsupportedEncodingException ex) {
350            ex.printStackTrace();
351            return new String();
352        }
353    }
354
355    /**
356     * Read raw bytes out of the buffer.
357     *
358     * @param count The maximum number of bytes to read.
359     * @return An array of the read bytes
360     */
361    public byte[] read(final int count) {
362            ByteBuffer status = ByteBuffer.allocateDirect(4);
363                status.order(ByteOrder.LITTLE_ENDIAN);
364                ByteBuffer dataReceivedBuffer = ByteBuffer.allocateDirect(count);
365        SerialPortJNI.serialRead(m_port, dataReceivedBuffer, count, status.asIntBuffer());
366                HALUtil.checkStatus(status.asIntBuffer());
367                byte[] retVal = new byte[count];
368                dataReceivedBuffer.get(retVal);
369                return retVal;
370    }
371
372    /**
373     * Write raw bytes to the serial port.
374     *
375     * @param buffer The buffer of bytes to write.
376     * @param count The maximum number of bytes to write.
377     * @return The number of bytes actually written into the port.
378     */
379    public int write(byte[] buffer, int count) {
380                ByteBuffer status = ByteBuffer.allocateDirect(4);
381                status.order(ByteOrder.LITTLE_ENDIAN);
382                ByteBuffer dataToSendBuffer = ByteBuffer.allocateDirect(count);
383                dataToSendBuffer.put(buffer, 0, count);
384        int retVal = SerialPortJNI.serialWrite(m_port, dataToSendBuffer, count, status.asIntBuffer());
385                HALUtil.checkStatus(status.asIntBuffer());
386                return retVal;
387    }
388        
389        /**
390     * Write a string to the serial port
391     *
392     * @param data The string to write to the serial port.
393     * @return The number of bytes actually written into the port.
394     */
395        public int writeString(String data) {
396                return write(data.getBytes(), data.length());
397        }
398
399    /**
400     * Configure the timeout of the serial port.
401     *
402     * This defines the timeout for transactions with the hardware.
403     * It will affect reads if less bytes are available than the
404     * read buffer size (defaults to 1) and very large writes.
405     *
406     * @param timeout The number of seconds to to wait for I/O.
407     */
408    public void setTimeout(double timeout) {
409        ByteBuffer status = ByteBuffer.allocateDirect(4);
410                status.order(ByteOrder.LITTLE_ENDIAN);
411                SerialPortJNI.serialSetTimeout(m_port, (float)timeout, status.asIntBuffer());
412                HALUtil.checkStatus(status.asIntBuffer());              
413    }
414
415    /**
416     * Specify the size of the input buffer.
417     *
418     * Specify the amount of data that can be stored before data
419     * from the device is returned to Read.  If you want
420     * data that is received to be returned immediately, set this to 1.
421     *
422     * It the buffer is not filled before the read timeout expires, all
423     * data that has been received so far will be returned.
424     *
425     * @param size The read buffer size.
426     */
427    public void setReadBufferSize(int size) {
428        ByteBuffer status = ByteBuffer.allocateDirect(4);
429                status.order(ByteOrder.LITTLE_ENDIAN);
430                SerialPortJNI.serialSetReadBufferSize(m_port, size, status.asIntBuffer());
431                HALUtil.checkStatus(status.asIntBuffer());              
432    }
433
434    /**
435    * Specify the size of the output buffer.
436    *
437    * Specify the amount of data that can be stored before being
438    * transmitted to the device.
439    *
440    * @param size The write buffer size.
441    */
442    public void setWriteBufferSize(int size) {
443        ByteBuffer status = ByteBuffer.allocateDirect(4);
444                status.order(ByteOrder.LITTLE_ENDIAN);
445                SerialPortJNI.serialSetWriteBufferSize(m_port, size, status.asIntBuffer());
446                HALUtil.checkStatus(status.asIntBuffer());              
447    }
448
449    /**
450     * Specify the flushing behavior of the output buffer.
451     *
452     * When set to kFlushOnAccess, data is synchronously written to the serial port
453     *   after each call to either print() or write().
454     *
455     * When set to kFlushWhenFull, data will only be written to the serial port when
456     *   the buffer is full or when flush() is called.
457     *
458     * @param mode The write buffer mode.
459     */
460    public void setWriteBufferMode(WriteBufferMode mode) {
461        ByteBuffer status = ByteBuffer.allocateDirect(4);
462                status.order(ByteOrder.LITTLE_ENDIAN);
463                SerialPortJNI.serialSetWriteMode(m_port, (byte)mode.value, status.asIntBuffer());
464                HALUtil.checkStatus(status.asIntBuffer());              
465    }
466
467    /**
468     * Force the output buffer to be written to the port.
469     *
470     * This is used when setWriteBufferMode() is set to kFlushWhenFull to force a
471     * flush before the buffer is full.
472     */
473    public void flush() {
474        ByteBuffer status = ByteBuffer.allocateDirect(4);
475                status.order(ByteOrder.LITTLE_ENDIAN);
476                SerialPortJNI.serialFlush(m_port, status.asIntBuffer());
477                HALUtil.checkStatus(status.asIntBuffer());              
478    }
479
480    /**
481     * Reset the serial port driver to a known state.
482     *
483     * Empty the transmit and receive buffers in the device and formatted I/O.
484     */
485    public void reset() {
486        ByteBuffer status = ByteBuffer.allocateDirect(4);
487                status.order(ByteOrder.LITTLE_ENDIAN);
488                SerialPortJNI.serialClear(m_port, status.asIntBuffer());
489                HALUtil.checkStatus(status.asIntBuffer());              
490    }
491}