testtools/SerialPort/SerialPort/SerialPort.cs (138 lines of code) (raw):
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Azure.IoT.Internal.Tools
{
/// <summary>
/// Provides a wrapper around Win32 API for serial port communication.
/// </summary>
/// <remarks>
/// Motivation for this wrapper:
/// The .Net 4.5 SerialPort currently has a bug in BreakState, making it unusable if this function is needed.
/// </remarks>
public class SerialPort : IDisposable
{
#region Data
private IntPtr m_fileHandle;
private string m_portName;
#endregion Data
public bool IsOpen
{
get { return m_fileHandle != IntPtr.Zero && m_fileHandle.ToInt32() != Win32Api.INVALID_HANDLE_VALUE; }
}
public SerialPort(string portName)
{
m_portName = portName;
}
/// <summary>
/// Opens the serial port for communication.
/// </summary>
/// <returns>True if the port is open successfully.</returns>
public bool Open()
{
m_fileHandle = Win32Api.CreateFile(m_portName);
return IsOpen;
}
/// <summary>
/// Closes the serial port for communication.
/// </summary>
/// <returns>True if the port is closed successfully.</returns>
public bool Close()
{
if (Win32Api.CloseHandle(m_fileHandle))
{
m_fileHandle = IntPtr.Zero;
}
return !IsOpen;
}
/// <summary>
/// Sets the serial port into break state.
/// </summary>
/// <remarks>SetBreakState() followed by ClearBrakeState() is equivalent to sending CTRL+BREAK.</remarks>
/// <returns>True if the state was set correctly, false otherwise.</returns>
public bool SetBreakState()
{
return Win32Api.SetCommBreak(m_fileHandle);
}
/// <summary>
/// Clears the break state set in the serial port.
/// </summary>
/// <returns>True if the state was set correctly, false otherwise.</returns>
public bool ClearBreakState()
{
return Win32Api.ClearCommBreak(m_fileHandle);
}
/// <summary>
/// Gets the timeouts currently set for the serial port communication.
/// </summary>
/// <returns>The timeouts currently set.</returns>
public SerialPortTimeouts GetTimeouts()
{
if (!IsOpen) throw new InvalidOperationException("Port is not open.");
SerialPortTimeouts commTimeouts = new SerialPortTimeouts();
if (!Win32Api.GetCommTimeouts(m_fileHandle, ref commTimeouts))
{
throw new Win32Exception(Win32Api.GetLastError(), "'GetCommTimeouts()' failed");
}
return commTimeouts;
}
/// <summary>
/// Sets the timeouts for the serial port communication.
/// </summary>
/// <param name="timeouts">Timeouts to be set.</param>
public void SetTimeouts(SerialPortTimeouts timeouts)
{
if (!Win32Api.SetCommTimeouts(m_fileHandle, ref timeouts))
{
throw new Win32Exception(Win32Api.GetLastError(), "'SetCommTimeouts()' failed");
}
}
/// <summary>
/// Sets the read timeouts to return only the contents in the buffer, returning immediatelly.
/// </summary>
public void SetDefaultTimeouts()
{
if (IsOpen)
{
SerialPortTimeouts timeouts = this.GetTimeouts();
timeouts.ReadIntervalTimeout = UInt32.MaxValue;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
this.SetTimeouts(timeouts);
}
}
/// <summary>
/// Gets the current state (config parameters) set on the serial port.
/// </summary>
/// <returns>The current serial port state.</returns>
public SerialPortState GetState()
{
if (!IsOpen) throw new InvalidOperationException("Port is not open.");
SerialPortState dcb = new SerialPortState();
if (!Win32Api.GetCommState(m_fileHandle, ref dcb))
{
throw new Win32Exception(Win32Api.GetLastError(), "'GetCommState()' failed");
}
return dcb;
}
/// <summary>
/// Sets the state (config parameters) on the serial port.
/// </summary>
/// <param name="state">The configuation to be applied to the serial port.</param>
public void SetState(SerialPortState state)
{
if (!IsOpen) throw new InvalidOperationException("Port is not open.");
if (!Win32Api.SetCommState(m_fileHandle, ref state))
{
throw new Win32Exception(Win32Api.GetLastError(), "'SetCommState()' failed");
}
}
/// <summary>
/// Reads data from the serial port.
/// </summary>
/// <param name="data">Array where to store the bytes read.</param>
/// <param name="offset">(Not used)</param>
/// <param name="count">Number of bytes to read.</param>
/// <returns>Number of bytes actually read.</returns>
public int Read(byte[] data, int offset, int count)
{
if (!IsOpen) throw new InvalidOperationException("Port is not open.");
uint bytesRead = 0;
if (!Win32Api.ReadFile(m_fileHandle, data, (uint)count, out bytesRead, IntPtr.Zero))
{
throw new Win32Exception(Win32Api.GetLastError(), "'ReadFile()' failed");
}
return (int)bytesRead;
}
/// <summary>
/// Reads text from the serial port.
/// </summary>
/// <returns>A string representing the data read from the serial port.</returns>
public string ReadLine()
{
StringBuilder message = new StringBuilder();
int bytesRead = 0;
byte[] data = new byte[1024];
while ((bytesRead = this.Read(data, 0, data.Length)) > 0)
{
message.Append(Encoding.ASCII.GetString(data, 0, bytesRead));
Thread.Sleep(50);
}
return message.ToString();
}
/// <summary>
/// Writes the text message, followed by CRLF.
/// </summary>
/// <param name="message">Text message to be written to the port.</param>
/// <param name="args">Arguments to be replaced on the message.</param>
/// <returns>True if the message was written successfully, false otherwise.</returns>
public bool WriteLine(string message, params object[] args)
{
message = args.Length > 0 ? String.Format(message, args) : message;
byte[] data = Encoding.ASCII.GetBytes(String.Format("{0}\r\n", message));
return (Write(data, 0, data.Length) > 0);
}
/// <summary>
/// Writes data to the serial port.
/// </summary>
/// <param name="data">The data to be written.</param>
/// <param name="offset">(Not used)</param>
/// <param name="count">Number of bytes from the data array to be written into the port.</param>
/// <returns>Number of bytes actually written.</returns>
public int Write(byte[] data, int offset, int count)
{
if (!IsOpen) throw new InvalidOperationException("Port is not open.");
uint bytesWritten = 0;
if (!Win32Api.WriteFile(m_fileHandle, data, (uint)count, out bytesWritten, IntPtr.Zero))
{
throw new Win32Exception(Win32Api.GetLastError(), "'WriteFile()' failed");
}
return (int)bytesWritten;
}
/// <summary>
/// Disposes the current serial port connection (closing it).
/// </summary>
public void Dispose()
{
if (IsOpen)
{
this.Close();
}
}
}
}