mattercontrol/FrostedSerial/FrostedSerialPort.cs
2014-01-29 19:09:30 -08:00

1002 lines
27 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.IO;
namespace MatterHackers.MatterControl.FrostedSerial
{
enum SerialSignal
{
None = 0,
Cd = 1, // Carrier detect
Cts = 2, // Clear to send
Dsr = 4, // Data set ready
Dtr = 8, // Data terminal ready
Rts = 16 // Request to send
}
public enum Handshake
{
None,
XOnXOff,
RequestToSend,
RequestToSendXOnXOff
}
public enum StopBits
{
None,
One,
Two,
OnePointFive
}
public enum Parity
{
None,
Odd,
Even,
Mark,
Space
}
public enum SerialError
{
RXOver = 1,
Overrun = 2,
RXParity = 4,
Frame = 8,
TXFull = 256
}
public enum SerialData
{
Chars = 1,
Eof
}
public enum SerialPinChange
{
CtsChanged = 8,
DsrChanged = 16,
CDChanged = 32,
Break = 64,
Ring = 256
}
public class SerialDataReceivedEventArgs : EventArgs
{
internal SerialDataReceivedEventArgs(SerialData eventType)
{
this.eventType = eventType;
}
// properties
public SerialData EventType
{
get
{
return eventType;
}
}
SerialData eventType;
}
public class SerialPinChangedEventArgs : EventArgs
{
internal SerialPinChangedEventArgs(SerialPinChange eventType)
{
this.eventType = eventType;
}
// properties
public SerialPinChange EventType
{
get
{
return eventType;
}
}
SerialPinChange eventType;
}
public class SerialErrorReceivedEventArgs : EventArgs
{
internal SerialErrorReceivedEventArgs(SerialError eventType)
{
this.eventType = eventType;
}
// properties
public SerialError EventType
{
get
{
return eventType;
}
}
SerialError eventType;
}
[MonitoringDescription("")]
[System.ComponentModel.DesignerCategory("")]
public class FrostedSerialPort : Component
{
public const int InfiniteTimeout = -1;
const int DefaultReadBufferSize = 4096;
const int DefaultWriteBufferSize = 2048;
const int DefaultBaudRate = 9600;
const int DefaultDataBits = 8;
const Parity DefaultParity = Parity.None;
const StopBits DefaultStopBits = StopBits.One;
bool is_open;
int baud_rate;
Parity parity;
StopBits stop_bits;
Handshake handshake;
int data_bits;
bool break_state = false;
bool dtr_enable = false;
bool rts_enable = false;
IFrostedSerialStream stream;
Encoding encoding = Encoding.ASCII;
string new_line = Environment.NewLine;
string port_name;
int read_timeout = InfiniteTimeout;
int write_timeout = InfiniteTimeout;
int readBufferSize = DefaultReadBufferSize;
int writeBufferSize = DefaultWriteBufferSize;
object error_received = new object();
object data_received = new object();
object pin_changed = new object();
public FrostedSerialPort() :
this(GetDefaultPortName(), DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
{
}
public FrostedSerialPort(IContainer container)
: this()
{
// TODO: What to do here?
}
public FrostedSerialPort(string portName) :
this(portName, DefaultBaudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
{
}
public FrostedSerialPort(string portName, int baudRate) :
this(portName, baudRate, DefaultParity, DefaultDataBits, DefaultStopBits)
{
}
public FrostedSerialPort(string portName, int baudRate, Parity parity) :
this(portName, baudRate, parity, DefaultDataBits, DefaultStopBits)
{
}
public FrostedSerialPort(string portName, int baudRate, Parity parity, int dataBits) :
this(portName, baudRate, parity, dataBits, DefaultStopBits)
{
}
public FrostedSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits)
{
port_name = portName;
baud_rate = baudRate;
data_bits = dataBits;
stop_bits = stopBits;
this.parity = parity;
}
static string GetDefaultPortName()
{
string[] ports = GetPortNames();
if (ports.Length > 0)
{
return ports[0];
}
else
{
int p = (int)Environment.OSVersion.Platform;
if (p == 4 || p == 128 || p == 6)
return "ttyS0"; // Default for Unix
else
return "COM1"; // Default for Windows
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public Stream BaseStream
{
get
{
CheckOpen();
return (Stream)stream;
}
}
[DefaultValueAttribute(DefaultBaudRate)]
[Browsable(true)]
[MonitoringDescription("")]
public int BaudRate
{
get
{
return baud_rate;
}
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.SetAttributes(value, parity, data_bits, stop_bits, handshake);
baud_rate = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool BreakState
{
get
{
return break_state;
}
set
{
CheckOpen();
if (value == break_state)
return; // Do nothing.
stream.SetBreakState(value);
break_state = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public int BytesToRead
{
get
{
CheckOpen();
return stream.BytesToRead;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public int BytesToWrite
{
get
{
CheckOpen();
return stream.BytesToWrite;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool CDHolding
{
get
{
CheckOpen();
return (stream.GetSignals() & SerialSignal.Cd) != 0;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool CtsHolding
{
get
{
CheckOpen();
return (stream.GetSignals() & SerialSignal.Cts) != 0;
}
}
[DefaultValueAttribute(DefaultDataBits)]
[Browsable(true)]
[MonitoringDescription("")]
public int DataBits
{
get
{
return data_bits;
}
set
{
if (value < 5 || value > 8)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.SetAttributes(baud_rate, parity, value, stop_bits, handshake);
data_bits = value;
}
}
//[MonoTODO("Not implemented")]
[Browsable(true)]
[MonitoringDescription("")]
[DefaultValue(false)]
public bool DiscardNull
{
get
{
throw new NotImplementedException();
}
set
{
// LAMESPEC: Msdn states that an InvalidOperationException exception
// is fired if the port is not open, which is *not* happening.
throw new NotImplementedException();
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool DsrHolding
{
get
{
CheckOpen();
return (stream.GetSignals() & SerialSignal.Dsr) != 0;
}
}
[DefaultValueAttribute(false)]
[Browsable(true)]
[MonitoringDescription("")]
public bool DtrEnable
{
get
{
return dtr_enable;
}
set
{
if (value == dtr_enable)
return;
if (is_open)
stream.SetSignal(SerialSignal.Dtr, value);
dtr_enable = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
[MonitoringDescription("")]
public Encoding Encoding
{
get
{
return encoding;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
encoding = value;
}
}
[DefaultValueAttribute(Handshake.None)]
[Browsable(true)]
[MonitoringDescription("")]
public Handshake Handshake
{
get
{
return handshake;
}
set
{
if (value < Handshake.None || value > Handshake.RequestToSendXOnXOff)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.SetAttributes(baud_rate, parity, data_bits, stop_bits, value);
handshake = value;
}
}
[Browsable(false)]
public bool IsOpen
{
get
{
return is_open;
}
}
[DefaultValueAttribute("\n")]
[Browsable(false)]
[MonitoringDescription("")]
public string NewLine
{
get
{
return new_line;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Length == 0)
throw new ArgumentException("NewLine cannot be null or empty.", "value");
new_line = value;
}
}
[DefaultValueAttribute(DefaultParity)]
[Browsable(true)]
[MonitoringDescription("")]
public Parity Parity
{
get
{
return parity;
}
set
{
if (value < Parity.None || value > Parity.Space)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.SetAttributes(baud_rate, value, data_bits, stop_bits, handshake);
parity = value;
}
}
//[MonoTODO("Not implemented")]
[Browsable(true)]
[MonitoringDescription("")]
[DefaultValue(63)]
public byte ParityReplace
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
[Browsable(true)]
[MonitoringDescription("")]
[DefaultValue("COM1")] // silly Windows-ism. We should ignore it.
public string PortName
{
get
{
return port_name;
}
set
{
if (is_open)
throw new InvalidOperationException("Port name cannot be set while port is open.");
if (value == null)
throw new ArgumentNullException("value");
if (value.Length == 0 || value.StartsWith("\\\\"))
throw new ArgumentException("value");
port_name = value;
}
}
[DefaultValueAttribute(DefaultReadBufferSize)]
[Browsable(true)]
[MonitoringDescription("")]
public int ReadBufferSize
{
get
{
return readBufferSize;
}
set
{
if (is_open)
throw new InvalidOperationException();
if (value <= 0)
throw new ArgumentOutOfRangeException("value");
if (value <= DefaultReadBufferSize)
return;
readBufferSize = value;
}
}
[DefaultValueAttribute(InfiniteTimeout)]
[Browsable(true)]
[MonitoringDescription("")]
public int ReadTimeout
{
get
{
return read_timeout;
}
set
{
if (value < 0 && value != InfiniteTimeout)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.ReadTimeout = value;
read_timeout = value;
}
}
//[MonoTODO("Not implemented")]
[DefaultValueAttribute(1)]
[Browsable(true)]
[MonitoringDescription("")]
public int ReceivedBytesThreshold
{
get
{
throw new NotImplementedException();
}
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException("value");
throw new NotImplementedException();
}
}
[DefaultValueAttribute(false)]
[Browsable(true)]
[MonitoringDescription("")]
public bool RtsEnable
{
get
{
return rts_enable;
}
set
{
if (value == rts_enable)
return;
if (is_open)
stream.SetSignal(SerialSignal.Rts, value);
rts_enable = value;
}
}
[DefaultValueAttribute(DefaultStopBits)]
[Browsable(true)]
[MonitoringDescription("")]
public StopBits StopBits
{
get
{
return stop_bits;
}
set
{
if (value < StopBits.One || value > StopBits.OnePointFive)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.SetAttributes(baud_rate, parity, data_bits, value, handshake);
stop_bits = value;
}
}
[DefaultValueAttribute(DefaultWriteBufferSize)]
[Browsable(true)]
[MonitoringDescription("")]
public int WriteBufferSize
{
get
{
return writeBufferSize;
}
set
{
if (is_open)
throw new InvalidOperationException();
if (value <= 0)
throw new ArgumentOutOfRangeException("value");
if (value <= DefaultWriteBufferSize)
return;
writeBufferSize = value;
}
}
[DefaultValueAttribute(InfiniteTimeout)]
[Browsable(true)]
[MonitoringDescription("")]
public int WriteTimeout
{
get
{
return write_timeout;
}
set
{
if (value < 0 && value != InfiniteTimeout)
throw new ArgumentOutOfRangeException("value");
if (is_open)
stream.WriteTimeout = value;
write_timeout = value;
}
}
// methods
public void Close()
{
Dispose(true);
}
protected override void Dispose(bool disposing)
{
if (!is_open)
return;
is_open = false;
// Do not close the base stream when the finalizer is run; the managed code can still hold a reference to it.
if (disposing)
stream.Close();
stream = null;
}
public void DiscardInBuffer()
{
CheckOpen();
stream.DiscardInBuffer();
}
public void DiscardOutBuffer()
{
CheckOpen();
stream.DiscardOutBuffer();
}
public static string[] GetPortNames()
{
int p = (int)Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
// Are we on Unix?
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = Directory.GetFiles("/dev/", "tty*");
bool linux_style = false;
//
// Probe for Linux-styled devices: /dev/ttyS* or /dev/ttyUSB*
//
foreach (string dev in ttys)
{
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB"))
{
linux_style = true;
break;
}
}
foreach (string dev in ttys)
{
if (linux_style)
{
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB"))
serial_ports.Add(dev);
}
else
{
if (dev != "/dev/tty" && dev.StartsWith("/dev/tty") && !dev.StartsWith("/dev/ttyC"))
serial_ports.Add(dev);
}
}
}
else
{
using (RegistryKey subkey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DEVICEMAP\\SERIALCOMM"))
{
if (subkey != null)
{
string[] names = subkey.GetValueNames();
foreach (string value in names)
{
string port = subkey.GetValue(value, "").ToString();
if (port != "")
serial_ports.Add(port);
}
}
}
}
return serial_ports.ToArray();
}
static bool IsWindows
{
get
{
PlatformID id = Environment.OSVersion.Platform;
return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
}
}
public void Open()
{
if (is_open)
throw new InvalidOperationException("Port is already open");
#if !TARGET_JVM
if (IsWindows) // Use windows kernel32 backend
stream = new WinSerialStream(port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
else // Use standard unix backend
#endif
stream = new FrostedSerialPortStream(port_name, baud_rate, data_bits, parity, stop_bits, dtr_enable,
rts_enable, handshake, read_timeout, write_timeout, readBufferSize, writeBufferSize);
is_open = true;
}
public int Read(byte[] buffer, int offset, int count)
{
CheckOpen();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset or count less than zero.");
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
return stream.Read(buffer, offset, count);
}
public int Read(char[] buffer, int offset, int count)
{
CheckOpen();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset or count less than zero.");
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
int c, i;
for (i = 0; i < count && (c = ReadChar()) != -1; i++)
buffer[offset + i] = (char)c;
return i;
}
internal int read_byte()
{
byte[] buff = new byte[1];
if (stream.Read(buff, 0, 1) > 0)
return buff[0];
return -1;
}
public int ReadByte()
{
CheckOpen();
return read_byte();
}
public int ReadChar()
{
CheckOpen();
byte[] buffer = new byte[16];
int i = 0;
do
{
int b = read_byte();
if (b == -1)
return -1;
buffer[i++] = (byte)b;
char[] c = encoding.GetChars(buffer, 0, 1);
if (c.Length > 0)
return (int)c[0];
} while (i < buffer.Length);
return -1;
}
public string ReadExisting()
{
CheckOpen();
int count = BytesToRead;
byte[] bytes = new byte[count];
int n = stream.Read(bytes, 0, count);
return new String(encoding.GetChars(bytes, 0, n));
}
public string ReadLine()
{
return ReadTo(new_line);
}
public string ReadTo(string value)
{
CheckOpen();
if (value == null)
throw new ArgumentNullException("value");
if (value.Length == 0)
throw new ArgumentException("value");
// Turn into byte array, so we can compare
byte[] byte_value = encoding.GetBytes(value);
int current = 0;
List<byte> seen = new List<byte>();
while (true)
{
int n = read_byte();
if (n == -1)
break;
seen.Add((byte)n);
if (n == byte_value[current])
{
current++;
if (current == byte_value.Length)
return encoding.GetString(seen.ToArray(), 0, seen.Count - byte_value.Length);
}
else
{
current = (byte_value[0] == n) ? 1 : 0;
}
}
return encoding.GetString(seen.ToArray());
}
public void Write(string str)
{
CheckOpen();
if (str == null)
throw new ArgumentNullException("str");
byte[] buffer = encoding.GetBytes(str);
Write(buffer, 0, buffer.Length);
}
public void Write(byte[] buffer, int offset, int count)
{
CheckOpen();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException();
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
stream.Write(buffer, offset, count);
}
public void Write(char[] buffer, int offset, int count)
{
CheckOpen();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException();
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
byte[] bytes = encoding.GetBytes(buffer, offset, count);
stream.Write(bytes, 0, bytes.Length);
}
public void WriteLine(string str)
{
Write(str + new_line);
}
void CheckOpen()
{
if (!is_open)
throw new InvalidOperationException("Specified port is not open.");
}
internal void OnErrorReceived(SerialErrorReceivedEventArgs args)
{
SerialErrorReceivedEventHandler handler =
(SerialErrorReceivedEventHandler)Events[error_received];
if (handler != null)
handler(this, args);
}
internal void OnDataReceived(SerialDataReceivedEventArgs args)
{
SerialDataReceivedEventHandler handler =
(SerialDataReceivedEventHandler)Events[data_received];
if (handler != null)
handler(this, args);
}
internal void OnDataReceived(SerialPinChangedEventArgs args)
{
SerialPinChangedEventHandler handler =
(SerialPinChangedEventHandler)Events[pin_changed];
if (handler != null)
handler(this, args);
}
// events
[MonitoringDescription("")]
public event SerialErrorReceivedEventHandler ErrorReceived
{
add { Events.AddHandler(error_received, value); }
remove { Events.RemoveHandler(error_received, value); }
}
[MonitoringDescription("")]
public event SerialPinChangedEventHandler PinChanged
{
add { Events.AddHandler(pin_changed, value); }
remove { Events.RemoveHandler(pin_changed, value); }
}
[MonitoringDescription("")]
public event SerialDataReceivedEventHandler DataReceived
{
add { Events.AddHandler(data_received, value); }
remove { Events.RemoveHandler(data_received, value); }
}
}
public delegate void SerialDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e);
public delegate void SerialPinChangedEventHandler(object sender, SerialPinChangedEventArgs e);
public delegate void SerialErrorReceivedEventHandler(object sender, SerialErrorReceivedEventArgs e);
}