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/*----------------------------------------------------------------------------*/
007
008/**
009 * Modified Driver for the RS-232 serial port on the cRIO.
010 *
011 * NOTE:  This is a modified version of the WPILIBJ SerialPort class,
012 * and resolves an issue wherein the read buffer length is forced to
013 * be 1 byte in length.  Creating a new class was required since access to
014 * the setRead
015 * 
016 * The current implementation uses the VISA formatted I/O mode.  This means that
017 *   all traffic goes through the formatted buffers.  This allows the intermingled
018 *   use of print(), readString(), and the raw buffer accessors read() and write().
019 *
020 * More information can be found in the NI-VISA User Manual here:
021 *   http://www.ni.com/pdf/manuals/370423a.pdf
022 * and the NI-VISA Programmer's Reference Manual here:
023 *   http://www.ni.com/pdf/manuals/370132c.pdf
024 */
025
026package com.kauailabs.nav6.frc;
027
028import edu.wpi.first.wpilibj.communication.UsageReporting;
029import edu.wpi.first.wpilibj.visa.Visa;
030import edu.wpi.first.wpilibj.visa.VisaException;
031import java.io.UnsupportedEncodingException;
032
033/**
034 * The BufferingSerialPort class is a replacement for the WPI Library
035 * SerialPort class.  As such, it is a modified Driver for the RS-232 
036 * serial port on the cRIO.
037 *
038 * NOTE:  This is a modified version of the WPILIBJ SerialPort class,
039 * and resolves an issue wherein the read buffer length is forced to
040 * be 1 byte in length.  Creating a new class was required to provide
041 * the setRead() method.
042 *
043 * @author Scott
044 */
045public class BufferingSerialPort {
046
047    private int m_resourceManagerHandle;
048    private int m_portHandle;
049
050    /**
051     * Represents the parity to use for serial communications
052     */
053    public static class Parity {
054
055        /**
056         * The integer value representing this enumeration
057         */
058        public final int value;
059        static final int kNone_val = 0;
060        static final int kOdd_val = 1;
061        static final int kEven_val = 2;
062        static final int kMark_val = 3;
063        static final int kSpace_val = 4;
064        /**
065         * parity: Use no parity
066         */
067        public static final Parity kNone = new Parity(kNone_val);
068        /**
069         * parity: Use odd parity
070         */
071        public static final Parity kOdd = new Parity(kOdd_val);
072        /**
073         * parity: Use even parity
074         */
075        public static final Parity kEven = new Parity(kEven_val);
076        /**
077         * parity: Use mark parity
078         */
079        public static final Parity kMark = new Parity(kMark_val);
080        /**
081         * parity: Use space parity
082         */
083        public static final Parity kSpace = new Parity((kSpace_val));
084
085        private Parity(int value) {
086            this.value = value;
087        }
088    }
089
090    /**
091     * Represents the number of stop bits to use for Serial Communication
092     */
093    public static class StopBits {
094
095        /**
096         * The integer value representing this enumeration
097         */
098        public final int value;
099        static final int kOne_val = 10;
100        static final int kOnePointFive_val = 15;
101        static final int kTwo_val = 20;
102        /**
103         * stopBits: use 1
104         */
105        public static final StopBits kOne = new StopBits(kOne_val);
106        /**
107         * stopBits: use 1.5
108         */
109        public static final StopBits kOnePointFive = new StopBits(kOnePointFive_val);
110        /**
111         * stopBits: use 2
112         */
113        public static final StopBits kTwo = new StopBits(kTwo_val);
114
115        private StopBits(int value) {
116            this.value = value;
117        }
118    }
119
120    /**
121     * Represents what type of flow control to use for serial communication
122     */
123    public static class FlowControl {
124
125        /**
126         * The integer value representing this enumeration
127         */
128        public final int value;
129        static final int kNone_val = 0;
130        static final int kXonXoff_val = 1;
131        static final int kRtsCts_val = 2;
132        static final int kDtrDsr_val = 4;
133        /**
134         * flowControl: use none
135         */
136        public static final FlowControl kNone = new FlowControl(kNone_val);
137        /**
138         * flowcontrol: use on/off
139         */
140        public static final FlowControl kXonXoff = new FlowControl(kXonXoff_val);
141        /**
142         * flowcontrol: use rts cts
143         */
144        public static final FlowControl kRtsCts = new FlowControl(kRtsCts_val);
145        /**
146         * flowcontrol: use dts dsr
147         */
148        public static final FlowControl kDtrDsr = new FlowControl(kDtrDsr_val);
149
150        private FlowControl(int value) {
151            this.value = value;
152        }
153    }
154
155    /**
156     * Represents which type of buffer mode to use when writing to a serial port
157     */
158    public static class WriteBufferMode {
159
160        /**
161         * The integer value representing this enumeration
162         */
163        public final int value;
164        static final int kFlushOnAccess_val = 1;
165        static final int kFlushWhenFull_val = 2;
166        /**
167         * Flush on access
168         */
169        public static final WriteBufferMode kFlushOnAccess = new WriteBufferMode(kFlushOnAccess_val);
170        /**
171         * Flush when full
172         */
173        public static final WriteBufferMode kFlushWhenFull = new WriteBufferMode(kFlushWhenFull_val);
174
175        private WriteBufferMode(int value) {
176            this.value = value;
177        }
178    }
179
180    /**
181     * Create an instance of a Serial Port class.
182     *
183     * @param baudRate The baud rate to configure the serial port.  The cRIO-9074 supports up to 230400 Baud.
184     * @param dataBits The number of data bits per transfer.  Valid values are between 5 and 8 bits.
185     * @param parity Select the type of parity checking to use.
186     * @param stopBits The number of stop bits to use as defined by the enum StopBits.
187     */
188    public BufferingSerialPort(final int baudRate, final int dataBits, Parity parity, StopBits stopBits) throws VisaException {
189        m_resourceManagerHandle = 0;
190        m_portHandle = 0;
191
192        m_resourceManagerHandle = Visa.viOpenDefaultRM();
193
194        m_portHandle = Visa.viOpen(m_resourceManagerHandle, "ASRL1::INSTR", 0, 0);
195        setFlowControl(BufferingSerialPort.FlowControl.kNone);
196        
197        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_BAUD, baudRate);
198
199        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_DATA_BITS, dataBits);
200
201        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_PARITY, parity.value);
202
203        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_STOP_BITS, stopBits.value);
204        
205        // Set the default read buffer size to 1 to return bytes immediately
206        setReadBufferSize(1);
207
208        // Set the default timeout to 5 seconds.
209        setTimeout(5.0f);
210
211        // Don't wait until the buffer is full to transmit.
212        setWriteBufferMode(WriteBufferMode.kFlushOnAccess);
213
214        disableTermination();
215
216        //viInstallHandler(m_portHandle, VI_EVENT_IO_COMPLETION, ioCompleteHandler, this);
217        //viEnableEvent(m_portHandle, VI_EVENT_IO_COMPLETION, VI_HNDLR, VI_NULL);
218
219        UsageReporting.report(UsageReporting.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.  The cRIO-9074 supports up to 230400 Baud.
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 BufferingSerialPort(final int baudRate, final int dataBits, Parity parity) throws VisaException {
230        this(baudRate, 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.  The cRIO-9074 supports up to 230400 Baud.
238     * @param dataBits The number of data bits per transfer.  Valid values are between 5 and 8 bits.
239     */
240    public BufferingSerialPort(final int baudRate, final int dataBits) throws VisaException {
241        this(baudRate, 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.  The cRIO-9074 supports up to 230400 Baud.
249     */
250    public BufferingSerialPort(final int baudRate) throws VisaException {
251        this(baudRate, 8, Parity.kNone, StopBits.kOne);
252    }
253
254    /**
255     * Destructor.
256     */
257    public void free() {
258        //viUninstallHandler(m_portHandle, VI_EVENT_IO_COMPLETION, ioCompleteHandler, this);
259        Visa.viClose(m_portHandle);
260        Visa.viClose(m_resourceManagerHandle);
261    }
262
263    /**
264     * Set the type of flow control to enable on this port.
265     *
266     * By default, flow control is disabled.
267     * @param flowControl
268     */
269    public void setFlowControl(FlowControl flowControl) throws VisaException {
270        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_FLOW_CNTRL, flowControl.value);
271    }
272
273    /**
274     * Enable termination and specify the termination character.
275     *
276     * Termination is currently only implemented for receive.
277     * When the the terminator is received, the read() or readString() will return
278     *   fewer bytes than requested, stopping after the terminator.
279     *
280     * @param terminator The character to use for termination.
281     */
282    public void enableTermination(char terminator) throws VisaException {
283        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_TERMCHAR_EN, true);
284        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_TERMCHAR, terminator);
285        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_END_IN, Visa.VI_ASRL_END_TERMCHAR);
286    }
287
288    /**
289     * Enable termination and specify the termination character.
290     *
291     * Termination is currently only implemented for receive.
292     * When the the terminator is received, the read() or readString() will return
293     *   fewer bytes than requested, stopping after the terminator.
294     *
295     * The default terminator is '\n'
296     */
297    public void enableTermination() throws VisaException {
298        this.enableTermination('\n');
299    }
300
301    /**
302     * Disable termination behavior.
303     */
304    public void disableTermination() throws VisaException {
305        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_TERMCHAR_EN, false);
306        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_END_IN, Visa.VI_ASRL_END_NONE);
307    }
308
309    /**
310     * Get the number of bytes currently available to read from the serial port.
311     *
312     * @return The number of bytes available to read.
313     */
314    public int getBytesReceived() throws VisaException {
315        return Visa.viGetAttribute(m_portHandle, Visa.VI_ATTR_ASRL_AVAIL_NUM);
316    }
317
318    /**
319     * Output formatted text to the serial port.
320     *
321     * @deprecated use write(string.getBytes()) instead
322     * @bug All pointer-based parameters seem to return an error.
323     *
324     * @param write A string to write
325     */
326    public void print(String write) throws VisaException {
327        Visa.viVPrintf(m_portHandle, write);
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() throws VisaException {
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) throws VisaException {
346        byte[] out = Visa.viBufRead(m_portHandle, 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) throws VisaException {
362        return Visa.viBufRead(m_portHandle, count);
363    }
364
365    /**
366     * Write raw bytes to the buffer.
367     *
368     * @param buffer the buffer to read the bytes from.
369     * @param count The maximum number of bytes to write.
370     * @return The number of bytes actually written into the port.
371     */
372    public int write(byte[] buffer, int count) throws VisaException {
373        return Visa.viBufWrite(m_portHandle, buffer, count);
374    }
375
376    /**
377     * Configure the timeout of the serial port.
378     *
379     * This defines the timeout for transactions with the hardware.
380     * It will affect reads if less bytes are available than the
381     * read buffer size (defaults to 1) and very large writes.
382     *
383     * @param timeout The number of seconds to to wait for I/O.
384     */
385    public void setTimeout(double timeout) throws VisaException {
386        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_TMO_VALUE, (int) (timeout * 1e3));
387    }
388    
389    /**
390     * Specify the size of the input buffer.
391     * 
392     * Specify the amount of data that can be stored before data
393     * from the device is returned to Read.  If you want
394     * data that is received to be returned immediately, set this to 1.
395     * 
396     * It the buffer is not filled before the read timeout expires, all
397     * data that has been received so far will be returned.
398     * 
399     * @param size The read buffer size.
400     */
401    public void setReadBufferSize(int size) throws VisaException
402    {
403       Visa.viSetBuf(m_portHandle, Visa.VI_READ_BUF, size);
404    }
405    
406    /**
407    * Specify the size of the output buffer.
408    * 
409    * Specify the amount of data that can be stored before being
410    * transmitted to the device.
411    * 
412    * @param size The write buffer size.
413    */
414    void setWriteBufferSize(int size) throws VisaException
415    {
416        Visa.viSetBuf(m_portHandle, Visa.VI_WRITE_BUF, size);
417    }
418
419    /**
420     * Specify the flushing behavior of the output buffer.
421     *
422     * When set to kFlushOnAccess, data is synchronously written to the serial port
423     *   after each call to either print() or write().
424     *
425     * When set to kFlushWhenFull, data will only be written to the serial port when
426     *   the buffer is full or when flush() is called.
427     *
428     * @param mode The write buffer mode.
429     */
430    public void setWriteBufferMode(WriteBufferMode mode) throws VisaException {
431        Visa.viSetAttribute(m_portHandle, Visa.VI_ATTR_WR_BUF_OPER_MODE, mode.value);
432    }
433    
434    /**
435     * Force the output buffer to be written to the port.
436     *
437     * This is used when setWriteBufferMode() is set to kFlushWhenFull to force a
438     * flush before the buffer is full.
439     */
440    public void flush() throws VisaException {
441        Visa.viFlush(m_portHandle, Visa.VI_WRITE_BUF);
442    }
443
444    /**
445     * Reset the serial port driver to a known state.
446     *
447     * Empty the transmit and receive buffers in the device and formatted I/O.
448     */
449    public void reset() throws VisaException {
450        Visa.viClear(m_portHandle);
451    }
452}